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