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/VulkanFramebuffer.cpp
Views: 1401
1
#include "Common/StringUtils.h"
2
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
3
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
4
5
static const char * const rpTypeDebugNames[] = {
6
"RENDER",
7
"RENDER_DEPTH",
8
"MV_RENDER",
9
"MV_RENDER_DEPTH",
10
"MS_RENDER",
11
"MS_RENDER_DEPTH",
12
"MS_MV_RENDER",
13
"MS_MV_RENDER_DEPTH",
14
"BACKBUF",
15
};
16
17
const char *GetRPTypeName(RenderPassType rpType) {
18
uint32_t index = (uint32_t)rpType;
19
if (index < ARRAY_SIZE(rpTypeDebugNames)) {
20
return rpTypeDebugNames[index];
21
} else {
22
return "N/A";
23
}
24
}
25
26
VkSampleCountFlagBits MultiSampleLevelToFlagBits(int count) {
27
// TODO: Check hardware support here, or elsewhere?
28
// Some hardware only supports 4x.
29
switch (count) {
30
case 0: return VK_SAMPLE_COUNT_1_BIT;
31
case 1: return VK_SAMPLE_COUNT_2_BIT;
32
case 2: return VK_SAMPLE_COUNT_4_BIT; // The only non-1 level supported on some mobile chips.
33
case 3: return VK_SAMPLE_COUNT_8_BIT;
34
case 4: return VK_SAMPLE_COUNT_16_BIT; // rare but exists, on Intel for example
35
default:
36
_assert_(false);
37
return VK_SAMPLE_COUNT_1_BIT;
38
}
39
}
40
41
void VKRImage::Delete(VulkanContext *vulkan) {
42
// Get rid of the views first, feels cleaner (but in reality doesn't matter).
43
if (rtView)
44
vulkan->Delete().QueueDeleteImageView(rtView);
45
if (texAllLayersView)
46
vulkan->Delete().QueueDeleteImageView(texAllLayersView);
47
for (int i = 0; i < 2; i++) {
48
if (texLayerViews[i]) {
49
vulkan->Delete().QueueDeleteImageView(texLayerViews[i]);
50
}
51
}
52
53
if (image) {
54
_dbg_assert_(alloc);
55
vulkan->Delete().QueueDeleteImageAllocation(image, alloc);
56
}
57
}
58
59
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VulkanBarrierBatch *barriers, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _multiSampleLevel, bool createDepthStencilBuffer, const char *tag)
60
: vulkan_(vk), tag_(tag), width(_width), height(_height), numLayers(_numLayers) {
61
62
_dbg_assert_(tag);
63
64
CreateImage(vulkan_, barriers, initCmd, color, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
65
if (createDepthStencilBuffer) {
66
CreateImage(vulkan_, barriers, initCmd, depth, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
67
}
68
69
if (_multiSampleLevel > 0) {
70
sampleCount = MultiSampleLevelToFlagBits(_multiSampleLevel);
71
72
// TODO: Create a different tag for these?
73
CreateImage(vulkan_, barriers, initCmd, msaaColor, width, height, numLayers, sampleCount, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
74
if (createDepthStencilBuffer) {
75
CreateImage(vulkan_, barriers, initCmd, msaaDepth, width, height, numLayers, sampleCount, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
76
}
77
} else {
78
sampleCount = VK_SAMPLE_COUNT_1_BIT;
79
}
80
81
UpdateTag(tag);
82
83
// We create the actual framebuffer objects on demand, because some combinations might not make sense.
84
// Framebuffer objects are just pointers to a set of images, so no biggie.
85
}
86
87
void VKRFramebuffer::UpdateTag(const char *newTag) {
88
char name[128];
89
snprintf(name, sizeof(name), "fb_color_%s", tag_.c_str());
90
vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, name);
91
vulkan_->SetDebugName(color.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, name);
92
if (depth.image) {
93
snprintf(name, sizeof(name), "fb_depth_%s", tag_.c_str());
94
vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, name);
95
vulkan_->SetDebugName(depth.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, name);
96
}
97
for (size_t rpType = 0; rpType < (size_t)RenderPassType::TYPE_COUNT; rpType++) {
98
if (framebuf[rpType]) {
99
snprintf(name, sizeof(name), "fb_%s", tag_.c_str());
100
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, name);
101
}
102
}
103
}
104
105
VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType) {
106
bool multiview = RenderPassTypeHasMultiView(rpType);
107
108
if (framebuf[(int)rpType]) {
109
return framebuf[(int)rpType];
110
}
111
112
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
113
VkImageView views[4]{};
114
115
bool hasDepth = RenderPassTypeHasDepth(rpType);
116
int attachmentCount = 0;
117
views[attachmentCount++] = color.rtView; // 2D array texture if multilayered.
118
if (hasDepth) {
119
if (!depth.rtView) {
120
WARN_LOG(Log::G3D, "depth render type to non-depth fb: %p %p fmt=%d (%s %dx%d)", (void *)depth.image, (void *)depth.texAllLayersView, depth.format, tag_.c_str(), width, height);
121
// Will probably crash, depending on driver.
122
}
123
views[attachmentCount++] = depth.rtView;
124
}
125
if (rpType & RenderPassType::MULTISAMPLE) {
126
views[attachmentCount++] = msaaColor.rtView;
127
if (hasDepth) {
128
views[attachmentCount++] = msaaDepth.rtView;
129
}
130
}
131
132
fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType, sampleCount);
133
fbci.attachmentCount = attachmentCount;
134
fbci.pAttachments = views;
135
fbci.width = width;
136
fbci.height = height;
137
fbci.layers = 1; // With multiview, this should be set as 1.
138
139
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType]);
140
_assert_(res == VK_SUCCESS);
141
142
if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) {
143
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
144
}
145
146
return framebuf[(int)rpType];
147
}
148
149
VKRFramebuffer::~VKRFramebuffer() {
150
color.Delete(vulkan_);
151
depth.Delete(vulkan_);
152
msaaColor.Delete(vulkan_);
153
msaaDepth.Delete(vulkan_);
154
155
for (auto &fb : framebuf) {
156
if (fb) {
157
vulkan_->Delete().QueueDeleteFramebuffer(fb);
158
}
159
}
160
}
161
162
// NOTE: If numLayers > 1, it will create an array texture, rather than a normal 2D texture.
163
// This requires a different sampling path!
164
void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VulkanBarrierBatch *barriers, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
165
// We don't support more exotic layer setups for now. Mono or stereo.
166
_dbg_assert_(numLayers == 1 || numLayers == 2);
167
168
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
169
ici.arrayLayers = numLayers;
170
ici.mipLevels = 1;
171
ici.extent.width = width;
172
ici.extent.height = height;
173
ici.extent.depth = 1;
174
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
175
ici.imageType = VK_IMAGE_TYPE_2D;
176
ici.samples = sampleCount;
177
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
178
ici.format = format;
179
ici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
180
if (sampleCount == VK_SAMPLE_COUNT_1_BIT) {
181
ici.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
182
}
183
if (color) {
184
ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
185
} else {
186
ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
187
}
188
189
VmaAllocationCreateInfo allocCreateInfo{};
190
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
191
VmaAllocationInfo allocInfo{};
192
193
VkResult res = vmaCreateImage(vulkan->Allocator(), &ici, &allocCreateInfo, &img.image, &img.alloc, &allocInfo);
194
_dbg_assert_(res == VK_SUCCESS);
195
196
vulkan->SetDebugName(img.image, VK_OBJECT_TYPE_IMAGE, tag);
197
198
VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
199
200
VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
201
ivci.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
202
ivci.format = ici.format;
203
ivci.image = img.image;
204
ivci.viewType = numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
205
ivci.subresourceRange.aspectMask = aspects;
206
ivci.subresourceRange.layerCount = numLayers;
207
ivci.subresourceRange.levelCount = 1;
208
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.rtView);
209
vulkan->SetDebugName(img.rtView, VK_OBJECT_TYPE_IMAGE_VIEW, tag);
210
_dbg_assert_(res == VK_SUCCESS);
211
212
// Separate view for texture sampling all layers together.
213
if (!color) {
214
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
215
}
216
217
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; // layered for consistency, even if single image.
218
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.texAllLayersView);
219
vulkan->SetDebugName(img.texAllLayersView, VK_OBJECT_TYPE_IMAGE_VIEW, tag);
220
221
// Create 2D views for both layers.
222
// Useful when multipassing shaders that don't yet exist in a single-pass-stereo version.
223
for (int i = 0; i < numLayers; i++) {
224
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
225
ivci.subresourceRange.layerCount = 1;
226
ivci.subresourceRange.baseArrayLayer = i;
227
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.texLayerViews[i]);
228
if (vulkan->DebugLayerEnabled()) {
229
char temp[128];
230
snprintf(temp, sizeof(temp), "%s_layer%d", tag, i);
231
vulkan->SetDebugName(img.texLayerViews[i], VK_OBJECT_TYPE_IMAGE_VIEW, temp);
232
}
233
_dbg_assert_(res == VK_SUCCESS);
234
}
235
236
VkPipelineStageFlags dstStage;
237
VkAccessFlagBits dstAccessMask;
238
switch (initialLayout) {
239
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
240
dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
241
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
242
break;
243
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
244
dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
245
dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
246
break;
247
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
248
dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
249
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
250
break;
251
default:
252
Crash();
253
return;
254
}
255
256
VkImageMemoryBarrier *barrier = barriers->Add(img.image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dstStage, 0);
257
barrier->subresourceRange.layerCount = numLayers;
258
barrier->subresourceRange.aspectMask = aspects;
259
barrier->oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
260
barrier->newLayout = initialLayout;
261
barrier->srcAccessMask = 0;
262
barrier->dstAccessMask = dstAccessMask;
263
264
img.layout = initialLayout;
265
img.format = format;
266
img.sampleCount = sampleCount;
267
img.tag = tag ? tag : "N/A";
268
img.numLayers = numLayers;
269
}
270
271
static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) {
272
switch (action) {
273
case VKRRenderPassLoadAction::CLEAR: return VK_ATTACHMENT_LOAD_OP_CLEAR;
274
case VKRRenderPassLoadAction::KEEP: return VK_ATTACHMENT_LOAD_OP_LOAD;
275
case VKRRenderPassLoadAction::DONT_CARE: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
276
}
277
return VK_ATTACHMENT_LOAD_OP_DONT_CARE; // avoid compiler warning
278
}
279
280
static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
281
switch (action) {
282
case VKRRenderPassStoreAction::STORE: return VK_ATTACHMENT_STORE_OP_STORE;
283
case VKRRenderPassStoreAction::DONT_CARE: return VK_ATTACHMENT_STORE_OP_DONT_CARE;
284
}
285
return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning
286
}
287
288
// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827
289
// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies
290
291
VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {
292
bool isBackbuffer = rpType == RenderPassType::BACKBUFFER;
293
bool hasDepth = RenderPassTypeHasDepth(rpType);
294
bool multiview = RenderPassTypeHasMultiView(rpType);
295
bool multisample = RenderPassTypeHasMultisample(rpType);
296
297
_dbg_assert_(!(isBackbuffer && multisample));
298
299
if (isBackbuffer) {
300
_dbg_assert_(key.depthLoadAction != VKRRenderPassLoadAction::KEEP);
301
}
302
303
if (multiview) {
304
// TODO: Assert that the device has multiview support enabled.
305
}
306
307
int colorAttachmentIndex = 0;
308
int depthAttachmentIndex = 1;
309
310
int attachmentCount = 0;
311
VkAttachmentDescription attachments[4]{};
312
attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
313
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
314
attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.colorLoadAction);
315
attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);
316
attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
317
attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
318
attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
319
attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
320
attachmentCount++;
321
322
if (hasDepth) {
323
attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
324
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
325
attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.depthLoadAction);
326
attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);
327
attachments[attachmentCount].stencilLoadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.stencilLoadAction);
328
attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
329
attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
330
attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
331
attachmentCount++;
332
}
333
334
if (multisample) {
335
colorAttachmentIndex = attachmentCount;
336
attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
337
attachments[attachmentCount].samples = sampleCount;
338
attachments[attachmentCount].loadOp = ConvertLoadAction(key.colorLoadAction);
339
attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);
340
attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
341
attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
342
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
343
attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
344
attachmentCount++;
345
346
if (hasDepth) {
347
depthAttachmentIndex = attachmentCount;
348
attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
349
attachments[attachmentCount].samples = sampleCount;
350
attachments[attachmentCount].loadOp = ConvertLoadAction(key.depthLoadAction);
351
attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);
352
attachments[attachmentCount].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
353
attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
354
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
355
attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
356
attachmentCount++;
357
}
358
}
359
360
VkAttachmentReference colorReference{};
361
colorReference.attachment = colorAttachmentIndex;
362
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
363
364
VkAttachmentReference depthReference{};
365
depthReference.attachment = depthAttachmentIndex;
366
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
367
368
VkSubpassDescription subpass{};
369
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
370
subpass.flags = 0;
371
subpass.colorAttachmentCount = 1;
372
subpass.pColorAttachments = &colorReference;
373
374
VkAttachmentReference colorResolveReference;
375
if (multisample) {
376
colorResolveReference.attachment = 0; // the non-msaa color buffer.
377
colorResolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
378
subpass.pResolveAttachments = &colorResolveReference;
379
} else {
380
subpass.pResolveAttachments = nullptr;
381
}
382
if (hasDepth) {
383
subpass.pDepthStencilAttachment = &depthReference;
384
}
385
subpass.preserveAttachmentCount = 0;
386
subpass.pPreserveAttachments = nullptr;
387
388
// Not sure if this is really necessary.
389
VkSubpassDependency deps[2]{};
390
size_t numDeps = 0;
391
392
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
393
rp.attachmentCount = attachmentCount;
394
rp.pAttachments = attachments;
395
rp.subpassCount = 1;
396
rp.pSubpasses = &subpass;
397
398
VkRenderPassMultiviewCreateInfoKHR mv{ VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR };
399
uint32_t viewMask = 0x3; // Must be outside the 'if (multiview)' scope!
400
int viewOffset = 0;
401
if (multiview) {
402
rp.pNext = &mv;
403
mv.subpassCount = 1;
404
mv.pViewMasks = &viewMask;
405
mv.dependencyCount = 0;
406
mv.pCorrelationMasks = &viewMask; // same masks
407
mv.correlationMaskCount = 1;
408
mv.pViewOffsets = &viewOffset;
409
}
410
411
if (isBackbuffer) {
412
// We don't specify any explicit transitions for these, so let's use subpass dependencies.
413
// This makes sure that writes to the depth image are done before we try to write to it again.
414
// From Sascha's examples.
415
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
416
deps[numDeps].dstSubpass = 0;
417
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
418
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
419
deps[numDeps].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
420
deps[numDeps].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
421
deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
422
numDeps++;
423
// Dependencies for the color image.
424
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
425
deps[numDeps].dstSubpass = 0;
426
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
427
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
428
deps[numDeps].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
429
deps[numDeps].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
430
deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
431
numDeps++;
432
}
433
434
if (numDeps > 0) {
435
rp.dependencyCount = (u32)numDeps;
436
rp.pDependencies = deps;
437
}
438
439
VkRenderPass pass;
440
VkResult res;
441
442
// We could always use renderpass2, but I think it'll get both paths better tested if we
443
// only use it with multisample enabled.
444
// if (vulkan->Extensions().KHR_create_renderpass2) {
445
if (multisample) {
446
// It's a bit unfortunate that we can't rely on vkCreateRenderPass2, because here we now have
447
// to do a bunch of struct conversion, just to not have to repeat the logic from above.
448
VkAttachmentDescription2KHR attachments2[4]{};
449
for (int i = 0; i < attachmentCount; i++) {
450
attachments2[i].sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
451
attachments2[i].format = attachments[i].format;
452
attachments2[i].samples = attachments[i].samples;
453
attachments2[i].loadOp = attachments[i].loadOp;
454
attachments2[i].storeOp = attachments[i].storeOp;
455
attachments2[i].stencilLoadOp = attachments[i].stencilLoadOp;
456
attachments2[i].stencilStoreOp = attachments[i].stencilStoreOp;
457
attachments2[i].initialLayout = attachments[i].initialLayout;
458
attachments2[i].finalLayout = attachments[i].finalLayout;
459
}
460
461
VkAttachmentReference2KHR colorReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
462
colorReference2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
463
colorReference2.attachment = colorReference.attachment;
464
colorReference2.layout = colorReference.layout;
465
466
VkAttachmentReference2KHR depthReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
467
depthReference2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
468
depthReference2.attachment = depthReference.attachment;
469
depthReference2.layout = depthReference.layout;
470
471
VkSubpassDependency2KHR deps2[2]{};
472
for (int i = 0; i < numDeps; i++) {
473
deps2[i].sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR;
474
deps2[i].dependencyFlags = deps[i].dependencyFlags;
475
deps2[i].srcAccessMask = deps[i].srcAccessMask;
476
deps2[i].dstAccessMask = deps[i].dstAccessMask;
477
deps2[i].srcStageMask = deps[i].srcStageMask;
478
deps2[i].dstStageMask = deps[i].dstStageMask;
479
deps2[i].srcSubpass = deps[i].srcSubpass;
480
deps2[i].dstSubpass = deps[i].dstSubpass;
481
deps2[i].dependencyFlags = deps[i].dependencyFlags;
482
deps2[i].viewOffset = 0;
483
}
484
485
VkAttachmentReference2KHR colorResolveReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
486
487
VkSubpassDescription2KHR subpass2{ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR };
488
subpass2.colorAttachmentCount = subpass.colorAttachmentCount;
489
subpass2.flags = subpass.flags;
490
subpass2.pColorAttachments = &colorReference2;
491
if (hasDepth) {
492
subpass2.pDepthStencilAttachment = &depthReference2;
493
}
494
subpass2.pipelineBindPoint = subpass.pipelineBindPoint;
495
subpass2.viewMask = multiview ? viewMask : 0;
496
if (multisample) {
497
colorResolveReference2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
498
colorResolveReference2.attachment = colorResolveReference.attachment; // the non-msaa color buffer.
499
colorResolveReference2.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
500
subpass2.pResolveAttachments = &colorResolveReference2;
501
} else {
502
subpass2.pResolveAttachments = nullptr;
503
}
504
505
VkAttachmentReference2KHR depthResolveReference2{ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR };
506
VkSubpassDescriptionDepthStencilResolveKHR depthStencilResolve{ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR };
507
if (hasDepth && multisample) {
508
ChainStruct(subpass2, &depthStencilResolve);
509
depthResolveReference2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
510
depthResolveReference2.attachment = 1;
511
depthResolveReference2.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
512
// TODO: Some games might benefit from the other depth resolve modes when depth texturing.
513
depthStencilResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR;
514
depthStencilResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR;
515
depthStencilResolve.pDepthStencilResolveAttachment = &depthResolveReference2;
516
}
517
518
VkRenderPassCreateInfo2KHR rp2{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR };
519
rp2.pAttachments = attachments2;
520
rp2.pDependencies = deps2;
521
rp2.attachmentCount = rp.attachmentCount;
522
rp2.dependencyCount = rp.dependencyCount;
523
rp2.correlatedViewMaskCount = multiview ? 1 : 0;
524
rp2.pCorrelatedViewMasks = multiview ? &viewMask : nullptr;
525
rp2.pSubpasses = &subpass2;
526
rp2.subpassCount = 1;
527
res = vkCreateRenderPass2(vulkan->GetDevice(), &rp2, nullptr, &pass);
528
} else {
529
res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass);
530
}
531
532
if (pass) {
533
vulkan->SetDebugName(pass, VK_OBJECT_TYPE_RENDER_PASS, GetRPTypeName(rpType));
534
}
535
536
_assert_(res == VK_SUCCESS);
537
_assert_(pass != VK_NULL_HANDLE);
538
return pass;
539
}
540
541
VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {
542
// When we create a render pass, we create all "types" of it immediately,
543
// practical later when referring to it. Could change to on-demand if it feels motivated
544
// but I think the render pass objects are cheap.
545
546
// WARNING: We don't include sampleCount in the key, there's only the distinction multisampled or not
547
// which comes from the rpType.
548
// So you CAN NOT mix and match different non-one sample counts.
549
550
_dbg_assert_(!((rpType & RenderPassType::MULTISAMPLE) && sampleCount == VK_SAMPLE_COUNT_1_BIT));
551
552
if (!pass[(int)rpType] || sampleCounts[(int)rpType] != sampleCount) {
553
if (pass[(int)rpType]) {
554
vulkan->Delete().QueueDeleteRenderPass(pass[(int)rpType]);
555
}
556
pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType, sampleCount);
557
sampleCounts[(int)rpType] = sampleCount;
558
}
559
return pass[(int)rpType];
560
}
561
562