Add VK_KHR_imageless_framebuffer
Under normal operations, a framebuffer is created with each VkImageView
it will need to use. This extension allows users to specify a flag that
tells the driver to wait until render pass begin time. The user must
also request this feature during device creation, this behavior is not
enabled by default.
Bug: b/167223759
Tests: dEQP-VK.imageless_framebuffer.*
Change-Id: I2be5cec4d8174e20372eb9e148a05034dd338341
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/48488
Tested-by: Sean Risser <srisser@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index 2fde442..e140825 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -1405,10 +1405,18 @@
}
void CommandBuffer::beginRenderPass(RenderPass *renderPass, Framebuffer *framebuffer, VkRect2D renderArea,
- uint32_t clearValueCount, const VkClearValue *clearValues, VkSubpassContents contents)
+ uint32_t clearValueCount, const VkClearValue *clearValues, VkSubpassContents contents,
+ const VkRenderPassAttachmentBeginInfo *attachmentInfo)
{
ASSERT(state == RECORDING);
+ if(attachmentInfo)
+ {
+ for(uint32_t i = 0; i < attachmentInfo->attachmentCount; i++)
+ {
+ framebuffer->setAttachment(vk::Cast(attachmentInfo->pAttachments[i]), i);
+ }
+ }
addCommand<::CmdBeginRenderPass>(renderPass, framebuffer, renderArea, clearValueCount, clearValues);
}
diff --git a/src/Vulkan/VkCommandBuffer.hpp b/src/Vulkan/VkCommandBuffer.hpp
index 9654514..7b6a61e 100644
--- a/src/Vulkan/VkCommandBuffer.hpp
+++ b/src/Vulkan/VkCommandBuffer.hpp
@@ -61,7 +61,8 @@
VkResult reset(VkCommandPoolResetFlags flags);
void beginRenderPass(RenderPass *renderPass, Framebuffer *framebuffer, VkRect2D renderArea,
- uint32_t clearValueCount, const VkClearValue *pClearValues, VkSubpassContents contents);
+ uint32_t clearValueCount, const VkClearValue *pClearValues, VkSubpassContents contents,
+ const VkRenderPassAttachmentBeginInfo *attachmentBeginInfo);
void nextSubpass(VkSubpassContents contents);
void endRenderPass();
void executeCommands(uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers);
diff --git a/src/Vulkan/VkFramebuffer.cpp b/src/Vulkan/VkFramebuffer.cpp
index 4c797e1..1ed2642 100644
--- a/src/Vulkan/VkFramebuffer.cpp
+++ b/src/Vulkan/VkFramebuffer.cpp
@@ -13,21 +13,54 @@
// limitations under the License.
#include "VkFramebuffer.hpp"
+
#include "VkImageView.hpp"
#include "VkRenderPass.hpp"
+#include "VkStringify.hpp"
+
#include <memory.h>
#include <algorithm>
namespace vk {
Framebuffer::Framebuffer(const VkFramebufferCreateInfo *pCreateInfo, void *mem)
- : attachmentCount(pCreateInfo->attachmentCount)
- , attachments(reinterpret_cast<ImageView **>(mem))
+ : attachments(reinterpret_cast<ImageView **>(mem))
, extent{ pCreateInfo->width, pCreateInfo->height, pCreateInfo->layers }
{
- for(uint32_t i = 0; i < attachmentCount; i++)
+ const VkBaseInStructure *curInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
+ const VkFramebufferAttachmentsCreateInfo *attachmentsCreateInfo = nullptr;
+ while(curInfo)
{
- attachments[i] = vk::Cast(pCreateInfo->pAttachments[i]);
+ switch(curInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO:
+ attachmentsCreateInfo = reinterpret_cast<const VkFramebufferAttachmentsCreateInfo *>(curInfo);
+ break;
+ default:
+ LOG_TRAP("pFramebufferCreateInfo->pNext->sType = %s", vk::Stringify(curInfo->sType).c_str());
+ break;
+ }
+ curInfo = curInfo->pNext;
+ }
+
+ if(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)
+ {
+ // If flags includes VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, the pNext chain **must**
+ // include a VkFramebufferAttachmentsCreateInfo.
+ ASSERT(attachmentsCreateInfo != nullptr);
+ attachmentCount = attachmentsCreateInfo->attachmentImageInfoCount;
+ for(uint32_t i = 0; i < attachmentCount; i++)
+ {
+ attachments[i] = nullptr;
+ }
+ }
+ else
+ {
+ attachmentCount = pCreateInfo->attachmentCount;
+ for(uint32_t i = 0; i < attachmentCount; i++)
+ {
+ attachments[i] = vk::Cast(pCreateInfo->pAttachments[i]);
+ }
}
}
@@ -115,6 +148,13 @@
}
}
+void Framebuffer::setAttachment(ImageView *imageView, uint32_t index)
+{
+ ASSERT(index < attachmentCount);
+ ASSERT(attachments[index] == nullptr);
+ attachments[index] = imageView;
+}
+
ImageView *Framebuffer::getAttachment(uint32_t index) const
{
return attachments[index];
@@ -147,7 +187,32 @@
size_t Framebuffer::ComputeRequiredAllocationSize(const VkFramebufferCreateInfo *pCreateInfo)
{
- return pCreateInfo->attachmentCount * sizeof(void *);
+ const VkBaseInStructure *curInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
+ const VkFramebufferAttachmentsCreateInfo *attachmentsInfo = nullptr;
+ while(curInfo)
+ {
+ switch(curInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO:
+ attachmentsInfo = reinterpret_cast<const VkFramebufferAttachmentsCreateInfo *>(curInfo);
+ break;
+ default:
+ LOG_TRAP("pFramebufferCreateInfo->pNext->sType = %s", vk::Stringify(curInfo->sType).c_str());
+ break;
+ }
+
+ curInfo = curInfo->pNext;
+ }
+
+ if(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)
+ {
+ ASSERT(attachmentsInfo != nullptr);
+ return attachmentsInfo->attachmentImageInfoCount * sizeof(void *);
+ }
+ else
+ {
+ return pCreateInfo->attachmentCount * sizeof(void *);
+ }
}
} // namespace vk
diff --git a/src/Vulkan/VkFramebuffer.hpp b/src/Vulkan/VkFramebuffer.hpp
index e9839ee..7ad1157 100644
--- a/src/Vulkan/VkFramebuffer.hpp
+++ b/src/Vulkan/VkFramebuffer.hpp
@@ -32,6 +32,7 @@
void clearAttachment(const RenderPass *renderPass, uint32_t subpassIndex, const VkClearAttachment &attachment, const VkClearRect &rect);
static size_t ComputeRequiredAllocationSize(const VkFramebufferCreateInfo *pCreateInfo);
+ void setAttachment(ImageView *imageView, uint32_t index);
ImageView *getAttachment(uint32_t index) const;
void resolve(const RenderPass *renderPass, uint32_t subpassIndex);
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 0d5c777..165db86 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -226,6 +226,12 @@
}
template<typename T>
+static void getPhysicalDeviceImagelessFramebufferFeatures(T *features)
+{
+ features->imagelessFramebuffer = VK_TRUE;
+}
+
+template<typename T>
static void getPhysicalDeviceVulkan12Features(T *features)
{
features->samplerMirrorClampToEdge = VK_FALSE;
@@ -257,7 +263,7 @@
features->runtimeDescriptorArray = VK_FALSE;
features->samplerFilterMinmax = VK_FALSE;
features->scalarBlockLayout = VK_FALSE;
- features->imagelessFramebuffer = VK_FALSE;
+ getPhysicalDeviceImagelessFramebufferFeatures(features);
features->uniformBufferStandardLayout = VK_FALSE;
features->shaderSubgroupExtendedTypes = VK_FALSE;
getPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR(features);
@@ -322,6 +328,9 @@
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT:
getPhysicalDeviceProvokingVertexFeaturesEXT(reinterpret_cast<VkPhysicalDeviceProvokingVertexFeaturesEXT *>(curExtension));
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES:
+ getPhysicalDeviceImagelessFramebufferFeatures(reinterpret_cast<VkPhysicalDeviceImagelessFramebufferFeatures *>(curExtension));
+ break;
default:
WARN("curExtension->pNext->sType = %s", vk::Stringify(curExtension->sType).c_str());
break;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 43cb19c..efb2bf4 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -379,7 +379,9 @@
{ VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, VK_EXT_PROVOKING_VERTEX_SPEC_VERSION },
{ VK_GOOGLE_SAMPLER_FILTERING_PRECISION_EXTENSION_NAME, VK_GOOGLE_SAMPLER_FILTERING_PRECISION_SPEC_VERSION },
{ VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION },
- { VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION }
+ // Vulkan 1.2 promoted extensions
+ { VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION },
+ { VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME, VK_KHR_IMAGELESS_FRAMEBUFFER_SPEC_VERSION }
};
static bool hasExtension(const char *extensionName, const VkExtensionProperties *extensionProperties, uint32_t extensionPropertiesCount)
@@ -828,6 +830,13 @@
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT:
ASSERT(!hasDeviceExtension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME));
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR:
+ {
+ const VkPhysicalDeviceImagelessFramebufferFeaturesKHR *imagelessFramebufferFeatures = reinterpret_cast<const VkPhysicalDeviceImagelessFramebufferFeaturesKHR *>(extensionCreateInfo);
+ // Always provide Imageless Framebuffers
+ (void)imagelessFramebufferFeatures->imagelessFramebuffer;
+ }
+ break;
default:
// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
LOG_TRAP("pCreateInfo->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
@@ -2145,18 +2154,6 @@
TRACE("(VkDevice device = %p, const VkFramebufferCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkFramebuffer* pFramebuffer = %p)",
device, pCreateInfo, pAllocator, pFramebuffer);
- if(pCreateInfo->flags != 0)
- {
- UNSUPPORTED("pCreateInfo->flags %d", int(pCreateInfo->flags));
- }
-
- auto *nextInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
- while(nextInfo)
- {
- LOG_TRAP("pCreateInfo->pNext sType = %s", vk::Stringify(nextInfo->sType).c_str());
- nextInfo = nextInfo->pNext;
- }
-
return vk::Framebuffer::Create(pAllocator, pCreateInfo, pFramebuffer);
}
@@ -2626,6 +2623,7 @@
commandBuffer, pRenderPassBegin, contents);
const VkBaseInStructure *renderPassBeginInfo = reinterpret_cast<const VkBaseInStructure *>(pRenderPassBegin->pNext);
+ const VkRenderPassAttachmentBeginInfo *attachmentBeginInfo = nullptr;
while(renderPassBeginInfo)
{
switch(renderPassBeginInfo->sType)
@@ -2635,6 +2633,9 @@
// in order to distribute rendering between multiple physical devices.
// SwiftShader only has a single physical device, so this extension does nothing in this case.
break;
+ case VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO:
+ attachmentBeginInfo = reinterpret_cast<const VkRenderPassAttachmentBeginInfo *>(renderPassBeginInfo);
+ break;
default:
LOG_TRAP("pRenderPassBegin->pNext sType = %s", vk::Stringify(renderPassBeginInfo->sType).c_str());
break;
@@ -2643,7 +2644,7 @@
renderPassBeginInfo = renderPassBeginInfo->pNext;
}
- vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, contents);
+ vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, contents, attachmentBeginInfo);
}
VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, const VkSubpassBeginInfoKHR *pSubpassBeginInfo)
@@ -2651,7 +2652,29 @@
TRACE("(VkCommandBuffer commandBuffer = %p, const VkRenderPassBeginInfo* pRenderPassBegin = %p, const VkSubpassBeginInfoKHR* pSubpassBeginInfo = %p)",
commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
- vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, pSubpassBeginInfo->contents);
+ const VkBaseInStructure *renderPassBeginInfo = reinterpret_cast<const VkBaseInStructure *>(pRenderPassBegin->pNext);
+ const VkRenderPassAttachmentBeginInfo *attachmentBeginInfo = nullptr;
+ while(renderPassBeginInfo)
+ {
+ switch(renderPassBeginInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO:
+ // This extension controls which render area is used on which physical device,
+ // in order to distribute rendering between multiple physical devices.
+ // SwiftShader only has a single physical device, so this extension does nothing in this case.
+ break;
+ case VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO:
+ attachmentBeginInfo = reinterpret_cast<const VkRenderPassAttachmentBeginInfo *>(renderPassBeginInfo);
+ break;
+ default:
+ LOG_TRAP("pRenderPassBegin->pNext sType = %s", vk::Stringify(renderPassBeginInfo->sType).c_str());
+ break;
+ }
+
+ renderPassBeginInfo = renderPassBeginInfo->pNext;
+ }
+
+ vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, pSubpassBeginInfo->contents, attachmentBeginInfo);
}
VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents)