Compare commits
1 Commits
gitIgnoreU
...
pf/vk-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7a82f7b3 |
@@ -88,6 +88,7 @@ public:
|
|||||||
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
|
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
|
||||||
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
|
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
|
||||||
VkExtent2D extent = {0, 0};
|
VkExtent2D extent = {0, 0};
|
||||||
|
bool protection = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageSyncData {
|
struct ImageSyncData {
|
||||||
@@ -199,6 +200,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool hasResized(SwapChainPtr handle);
|
virtual bool hasResized(SwapChainPtr handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the surface is protected.
|
||||||
|
* @param handle The handle returned by createSwapChain()
|
||||||
|
* @return Whether the swapchain is protected
|
||||||
|
*/
|
||||||
|
virtual bool isProtected(SwapChainPtr handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Carry out a recreation of the swapchain.
|
* Carry out a recreation of the swapchain.
|
||||||
* @param handle The handle returned by createSwapChain()
|
* @param handle The handle returned by createSwapChain()
|
||||||
@@ -267,6 +275,23 @@ public:
|
|||||||
*/
|
*/
|
||||||
VkQueue getGraphicsQueue() const noexcept;
|
VkQueue getGraphicsQueue() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The family index of the protected graphics queue selected for the
|
||||||
|
* Vulkan backend.
|
||||||
|
*/
|
||||||
|
uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The index of the protected graphics queue (if there are multiple
|
||||||
|
* graphics queues) selected for the Vulkan backend.
|
||||||
|
*/
|
||||||
|
uint32_t getProtectedGraphicsQueueIndex() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The protected queue that was selected for the Vulkan backend.
|
||||||
|
*/
|
||||||
|
VkQueue getProtectedGraphicsQueue() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static ExtensionSet getSwapchainInstanceExtensions();
|
static ExtensionSet getSwapchainInstanceExtensions();
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,8 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VulkanCommandBuffer& commands = mCommands->get();
|
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
|
||||||
|
mCommands->getProtected() : mCommands->get();
|
||||||
commands.acquire(src.texture);
|
commands.acquire(src.texture);
|
||||||
commands.acquire(dst.texture);
|
commands.acquire(dst.texture);
|
||||||
resolveFast(&commands, aspect, src, dst);
|
resolveFast(&commands, aspect, src, dst);
|
||||||
@@ -176,7 +177,8 @@ void VulkanBlitter::blit(VkFilter filter,
|
|||||||
#endif
|
#endif
|
||||||
// src and dst should have the same aspect here
|
// src and dst should have the same aspect here
|
||||||
VkImageAspectFlags const aspect = src.texture->getImageAspect();
|
VkImageAspectFlags const aspect = src.texture->getImageAspect();
|
||||||
VulkanCommandBuffer& commands = mCommands->get();
|
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
|
||||||
|
mCommands->getProtected() : mCommands->get();
|
||||||
commands.acquire(src.texture);
|
commands.acquire(src.texture);
|
||||||
commands.acquire(dst.texture);
|
commands.acquire(dst.texture);
|
||||||
blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair);
|
blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair);
|
||||||
|
|||||||
@@ -33,48 +33,28 @@ using namespace utils;
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||||
using Timestamp = VulkanGroupMarkers::Timestamp;
|
using Timestamp = VulkanGroupMarkers::Timestamp;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VulkanCmdFence::VulkanCmdFence(VkFence ifence)
|
VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
|
||||||
: fence(ifence) {
|
VkCommandBuffer cmdbuffer;
|
||||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence gets
|
|
||||||
// submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually finishes
|
|
||||||
// executing the command buffer, the status changes to VK_SUCCESS.
|
|
||||||
status.store(VK_INCOMPLETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device,
|
|
||||||
VkCommandPool pool)
|
|
||||||
: mResourceManager(allocator),
|
|
||||||
mPipeline(VK_NULL_HANDLE) {
|
|
||||||
// Create the low-level command buffer.
|
// Create the low-level command buffer.
|
||||||
const VkCommandBufferAllocateInfo allocateInfo{
|
VkCommandBufferAllocateInfo const allocateInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
.commandPool = pool,
|
.commandPool = pool,
|
||||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||||
.commandBufferCount = 1,
|
.commandBufferCount = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called.
|
// The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called.
|
||||||
// We don't need to deallocate since destroying the pool will free all of the buffers.
|
// We don't need to deallocate since destroying the pool will free all of the buffers.
|
||||||
vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer);
|
vkAllocateCommandBuffers(device, &allocateInfo, &cmdbuffer);
|
||||||
|
return cmdbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandBufferObserver::~CommandBufferObserver() {}
|
} // anonymous namespace
|
||||||
|
|
||||||
static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) {
|
|
||||||
VkCommandPoolCreateInfo createInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
||||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
|
|
||||||
| VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
|
||||||
.queueFamilyIndex = queueFamilyIndex,
|
|
||||||
};
|
|
||||||
VkCommandPool pool;
|
|
||||||
vkCreateCommandPool(device, &createInfo, VKALLOC, &pool);
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||||
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
|
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
|
||||||
@@ -130,178 +110,147 @@ bool VulkanGroupMarkers::empty() const noexcept {
|
|||||||
|
|
||||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||||
|
|
||||||
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator,
|
||||||
VulkanContext* context, VulkanResourceAllocator* allocator)
|
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected)
|
||||||
: mDevice(device),
|
: mContext(context),
|
||||||
|
mMarkerCount(0),
|
||||||
|
isProtected(isProtected),
|
||||||
|
mDevice(device),
|
||||||
mQueue(queue),
|
mQueue(queue),
|
||||||
mPool(createPool(mDevice, queueFamilyIndex)),
|
mResourceManager(allocator),
|
||||||
mContext(context),
|
mBuffer(createCommandBuffer(device, pool)),
|
||||||
mStorage(CAPACITY) {
|
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
|
||||||
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||||
for (auto& semaphore: mSubmissionSignals) {
|
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
|
||||||
vkCreateSemaphore(mDevice, &sci, nullptr, &semaphore);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
||||||
for (auto& fence: mFences) {
|
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
|
||||||
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
|
mIsProtected = isProtected;
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < CAPACITY; ++i) {
|
|
||||||
mStorage[i] = std::make_unique<VulkanCommandBuffer>(allocator, mDevice, mPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
|
||||||
(void) mContext;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommands::terminate() {
|
VulkanCommandBuffer::~VulkanCommandBuffer() {
|
||||||
wait();
|
vkDestroySemaphore(mDevice, mSubmission, VKALLOC);
|
||||||
gc();
|
vkDestroyFence(mDevice, mFence, VKALLOC);
|
||||||
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
|
}
|
||||||
for (VkSemaphore sema: mSubmissionSignals) {
|
|
||||||
vkDestroySemaphore(mDevice, sema, VKALLOC);
|
void VulkanCommandBuffer::reset() noexcept {
|
||||||
|
mMarkerCount = 0;
|
||||||
|
mResourceManager.clear();
|
||||||
|
mWaitSemaphores.clear();
|
||||||
|
|
||||||
|
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||||
|
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||||
|
// finishes executing the command buffer, the status changes to VK_SUCCESS.
|
||||||
|
mFenceStatus = std::make_shared<VulkanCmdFence>(VK_INCOMPLETE);
|
||||||
|
vkResetFences(mDevice, 1, &mFence);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanCommandBuffer::pushMarker(char const* marker) noexcept {
|
||||||
|
if (mContext->isDebugUtilsSupported()) {
|
||||||
|
VkDebugUtilsLabelEXT labelInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
|
||||||
|
.pLabelName = marker,
|
||||||
|
.color = {0, 1, 0, 1},
|
||||||
|
};
|
||||||
|
vkCmdBeginDebugUtilsLabelEXT(mBuffer, &labelInfo);
|
||||||
|
} else if (mContext->isDebugMarkersSupported()) {
|
||||||
|
VkDebugMarkerMarkerInfoEXT markerInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
|
||||||
|
.pMarkerName = marker,
|
||||||
|
.color = {0.0f, 1.0f, 0.0f, 1.0f},
|
||||||
|
};
|
||||||
|
vkCmdDebugMarkerBeginEXT(mBuffer, &markerInfo);
|
||||||
}
|
}
|
||||||
for (VkFence fence: mFences) {
|
mMarkerCount++;
|
||||||
vkDestroyFence(mDevice, fence, VKALLOC);
|
}
|
||||||
|
|
||||||
|
void VulkanCommandBuffer::popMarker() noexcept{
|
||||||
|
assert_invariant(mMarkerCount > 0);
|
||||||
|
if (mContext->isDebugUtilsSupported()) {
|
||||||
|
vkCmdEndDebugUtilsLabelEXT(mBuffer);
|
||||||
|
} else if (mContext->isDebugMarkersSupported()) {
|
||||||
|
vkCmdDebugMarkerEndEXT(mBuffer);
|
||||||
|
}
|
||||||
|
mMarkerCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanCommandBuffer::insertEvent(char const* marker) noexcept {
|
||||||
|
if (mContext->isDebugUtilsSupported()) {
|
||||||
|
VkDebugUtilsLabelEXT labelInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
|
||||||
|
.pLabelName = marker,
|
||||||
|
.color = {1, 1, 0, 1},
|
||||||
|
};
|
||||||
|
vkCmdInsertDebugUtilsLabelEXT(mBuffer, &labelInfo);
|
||||||
|
} else if (mContext->isDebugMarkersSupported()) {
|
||||||
|
VkDebugMarkerMarkerInfoEXT markerInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
|
||||||
|
.pMarkerName = marker,
|
||||||
|
.color = {0.0f, 1.0f, 0.0f, 1.0f},
|
||||||
|
};
|
||||||
|
vkCmdDebugMarkerInsertEXT(mBuffer, &markerInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanCommandBuffer& VulkanCommands::get() {
|
void VulkanCommandBuffer::begin() noexcept {
|
||||||
if (mCurrentCommandBufferIndex >= 0) {
|
FVK_LOGE <<"[" << (mIsProtected ? "protected" : "regular") <<"]=" << buffer() << " begin"
|
||||||
return *mStorage[mCurrentCommandBufferIndex].get();
|
<<utils::io::endl;
|
||||||
}
|
|
||||||
|
|
||||||
// If we ran out of available command buffers, stall until one finishes. This is very rare.
|
|
||||||
// It occurs only when Filament invokes commit() or endFrame() a large number of times without
|
|
||||||
// presenting the swap chain or waiting on a fence.
|
|
||||||
while (mAvailableBufferCount == 0) {
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
|
||||||
FVK_LOGI << "VulkanCommands has stalled. "
|
|
||||||
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
|
|
||||||
<< io::endl;
|
|
||||||
#endif
|
|
||||||
wait();
|
|
||||||
gc();
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanCommandBuffer* currentbuf = nullptr;
|
|
||||||
// Find an available slot.
|
|
||||||
for (size_t i = 0; i < CAPACITY; ++i) {
|
|
||||||
auto wrapper = mStorage[i].get();
|
|
||||||
if (wrapper->buffer() == VK_NULL_HANDLE) {
|
|
||||||
mCurrentCommandBufferIndex = static_cast<int8_t>(i);
|
|
||||||
currentbuf = wrapper;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_invariant(currentbuf);
|
|
||||||
mAvailableBufferCount--;
|
|
||||||
|
|
||||||
// Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership
|
|
||||||
// over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in
|
|
||||||
// VulkanDriver::destroyFence(), both of which are safe spots.
|
|
||||||
currentbuf->fence = std::make_shared<VulkanCmdFence>(mFences[mCurrentCommandBufferIndex]);
|
|
||||||
|
|
||||||
// Begin writing into the command buffer.
|
// Begin writing into the command buffer.
|
||||||
const VkCommandBufferBeginInfo binfo{
|
VkCommandBufferBeginInfo const binfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||||
};
|
};
|
||||||
vkBeginCommandBuffer(currentbuf->buffer(), &binfo);
|
vkBeginCommandBuffer(mBuffer, &binfo);
|
||||||
|
|
||||||
// Notify the observer that a new command buffer has been activated.
|
|
||||||
if (mObserver) {
|
|
||||||
mObserver->onCommandBuffer(*currentbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
|
||||||
// We push the current markers onto a temporary stack. This must be placed after currentbuf is
|
|
||||||
// set to the new command buffer since pushGroupMarker also calls get().
|
|
||||||
while (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
|
|
||||||
auto [marker, time] = mCarriedOverMarkers->pop();
|
|
||||||
pushGroupMarker(marker.c_str(), time);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return *currentbuf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanCommands::flush() {
|
VkSemaphore VulkanCommandBuffer::submit() {
|
||||||
// It's perfectly fine to call flush when no commands have been written.
|
while (mMarkerCount > 0) {
|
||||||
if (mCurrentCommandBufferIndex < 0) {
|
popMarker();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before actually submitting, we need to pop any leftover group markers.
|
vkEndCommandBuffer(mBuffer);
|
||||||
// Note that this needs to occur before vkEndCommandBuffer.
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
|
||||||
while (mGroupMarkers && !mGroupMarkers->empty()) {
|
|
||||||
if (!mCarriedOverMarkers) {
|
|
||||||
mCarriedOverMarkers = std::make_unique<VulkanGroupMarkers>();
|
|
||||||
}
|
|
||||||
auto const [marker, time] = mGroupMarkers->top();
|
|
||||||
mCarriedOverMarkers->push(marker, time);
|
|
||||||
// We still need to call through to vkCmdEndDebugUtilsLabelEXT.
|
|
||||||
popGroupMarker();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int8_t const index = mCurrentCommandBufferIndex;
|
FVK_LOGE <<"[" << (mIsProtected ? "protected" : "regular") <<"]=" << buffer() << " end"
|
||||||
VulkanCommandBuffer const* currentbuf = mStorage[index].get();
|
<<utils::io::endl;
|
||||||
VkSemaphore const renderingFinished = mSubmissionSignals[index];
|
|
||||||
|
|
||||||
vkEndCommandBuffer(currentbuf->buffer());
|
VkPipelineStageFlags const waitDestStageMasks[2] = {
|
||||||
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
// If the injected semaphore is an "image available" semaphore that has not yet been signaled,
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
// it is sometimes fine to start executing commands anyway, as along as we stall the GPU at the
|
|
||||||
// VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT stage. However we need to assume the worst
|
|
||||||
// here and use VK_PIPELINE_STAGE_ALL_COMMANDS_BIT. This is a more aggressive stall, but it is
|
|
||||||
// the only safe option because the previously submitted command buffer might have set up some
|
|
||||||
// state that the new command buffer depends on.
|
|
||||||
VkPipelineStageFlags waitDestStageMasks[2] = {
|
|
||||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
||||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VkSemaphore signals[2] = {
|
|
||||||
VK_NULL_HANDLE,
|
|
||||||
VK_NULL_HANDLE,
|
|
||||||
};
|
|
||||||
uint32_t waitSemaphoreCount = 0;
|
|
||||||
if (mSubmissionSignal) {
|
|
||||||
signals[waitSemaphoreCount++] = mSubmissionSignal;
|
|
||||||
}
|
|
||||||
if (mInjectedSignal) {
|
|
||||||
signals[waitSemaphoreCount++] = mInjectedSignal;
|
|
||||||
}
|
|
||||||
VkCommandBuffer const cmdbuffer = currentbuf->buffer();
|
|
||||||
VkSubmitInfo submitInfo{
|
VkSubmitInfo submitInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
.waitSemaphoreCount = waitSemaphoreCount,
|
.waitSemaphoreCount = mWaitSemaphores.size(),
|
||||||
.pWaitSemaphores = waitSemaphoreCount > 0 ? signals : nullptr,
|
.pWaitSemaphores = mWaitSemaphores.data(),
|
||||||
.pWaitDstStageMask = waitDestStageMasks,
|
.pWaitDstStageMask = waitDestStageMasks,
|
||||||
.commandBufferCount = 1,
|
.commandBufferCount = 1u,
|
||||||
.pCommandBuffers = &cmdbuffer,
|
.pCommandBuffers = &mBuffer,
|
||||||
.signalSemaphoreCount = 1u,
|
.signalSemaphoreCount = 1u,
|
||||||
.pSignalSemaphores = &renderingFinished,
|
.pSignalSemaphores = &mSubmission,
|
||||||
};
|
};
|
||||||
|
// add submit protection if needed
|
||||||
|
VkProtectedSubmitInfo protectedSubmitInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO,
|
||||||
|
.protectedSubmit = VK_TRUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isProtected) {
|
||||||
|
submitInfo.pNext = &protectedSubmitInfo;
|
||||||
|
}
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||||
FVK_LOGI << "Submitting cmdbuffer=" << cmdbuffer
|
FVK_LOGI << "Submitting cmdbuffer=" << mBuffer
|
||||||
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
|
<< " wait=(";
|
||||||
<< " signal=" << renderingFinished
|
for (size_t s = 0, count = mWaitSemaphores.size(); s < count; ++s) {
|
||||||
<< " fence=" << currentbuf->fence->fence
|
FVK_LOGI << mWaitSemaphores[s] << " ";
|
||||||
<< utils::io::endl;
|
}
|
||||||
|
FVK_LOGI << ") "
|
||||||
|
<< " signal=" << mSubmission
|
||||||
|
<< " fence=" << mFence << utils::io::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto& cmdfence = currentbuf->fence;
|
mFenceStatus->setStatus(VK_NOT_READY);
|
||||||
UTILS_UNUSED_IN_RELEASE VkResult result = VK_SUCCESS;
|
VkResult result = vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
|
||||||
{
|
|
||||||
auto scope = cmdfence->setValue(VK_NOT_READY);
|
|
||||||
result = vkQueueSubmit(mQueue, 1, &submitInfo, cmdfence->getFence());
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
@@ -309,86 +258,212 @@ bool VulkanCommands::flush() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
assert_invariant(result == VK_SUCCESS);
|
assert_invariant(result == VK_SUCCESS);
|
||||||
|
mWaitSemaphores.clear();
|
||||||
|
return mSubmission;
|
||||||
|
}
|
||||||
|
|
||||||
mSubmissionSignal = renderingFinished;
|
CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator,
|
||||||
mInjectedSignal = VK_NULL_HANDLE;
|
VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected)
|
||||||
mCurrentCommandBufferIndex = -1;
|
: mDevice(device),
|
||||||
|
mRecording(INVALID) {
|
||||||
|
VkCommandPoolCreateInfo createInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||||
|
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT |
|
||||||
|
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
|
||||||
|
(isProtected ? VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0u),
|
||||||
|
.queueFamilyIndex = queueFamilyIndex,
|
||||||
|
};
|
||||||
|
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < CAPACITY; ++i) {
|
||||||
|
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, allocator, device,
|
||||||
|
queue, mPool, isProtected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBufferPool::~CommandBufferPool() {
|
||||||
|
wait();
|
||||||
|
gc();
|
||||||
|
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanCommandBuffer& CommandBufferPool::getRecording() {
|
||||||
|
if (isRecording()) {
|
||||||
|
return *mBuffers[mRecording];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const findNext = [this]() {
|
||||||
|
for (int8_t i = 0; i < CAPACITY; ++i) {
|
||||||
|
if (!mSubmitted[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INVALID;
|
||||||
|
};
|
||||||
|
|
||||||
|
while ((mRecording = findNext()) == INVALID) {
|
||||||
|
wait();
|
||||||
|
gc();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& recording = *mBuffers[mRecording];
|
||||||
|
recording.begin();
|
||||||
|
return recording;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::gc() {
|
||||||
|
ActiveBuffers reclaimed;
|
||||||
|
mSubmitted.forEachSetBit([this,&reclaimed] (size_t index) {
|
||||||
|
auto& buffer = mBuffers[index];
|
||||||
|
if (buffer->getStatus() == VK_SUCCESS) {
|
||||||
|
reclaimed.set(index, true);
|
||||||
|
buffer->reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mSubmitted &= ~reclaimed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::update() {
|
||||||
|
mSubmitted.forEachSetBit([this] (size_t index) {
|
||||||
|
auto& buffer = mBuffers[index];
|
||||||
|
VkResult status = vkGetFenceStatus(mDevice, buffer->getVkFence());
|
||||||
|
if (status == VK_SUCCESS) {
|
||||||
|
buffer->setComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphore CommandBufferPool::flush() {
|
||||||
|
// We're not recording right now.
|
||||||
|
if (!isRecording()) {
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
auto submitSemaphore = mBuffers[mRecording]->submit();
|
||||||
|
mSubmitted.set(mRecording, true);
|
||||||
|
mRecording = INVALID;
|
||||||
|
return submitSemaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::wait() {
|
||||||
|
uint8_t count = 0;
|
||||||
|
VkFence fences[CAPACITY];
|
||||||
|
mSubmitted.forEachSetBit([this,&count,&fences] (size_t index) {
|
||||||
|
fences[count++] = mBuffers[index]->getVkFence();
|
||||||
|
});
|
||||||
|
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::waitFor(VkSemaphore previousAction) {
|
||||||
|
if (!isRecording()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& recording = mBuffers[mRecording];
|
||||||
|
recording->insertWait(previousAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::pushMarker(char const* marker) {
|
||||||
|
getRecording().pushMarker(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::popMarker() {
|
||||||
|
getRecording().popMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferPool::insertEvent(char const* marker) {
|
||||||
|
getRecording().insertEvent(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||||
|
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
|
||||||
|
VulkanResourceAllocator* allocator)
|
||||||
|
: mDevice(device),
|
||||||
|
mProtectedQueue(protectedQueue),
|
||||||
|
mProtectedQueueFamilyIndex(protectedQueueFamilyIndex),
|
||||||
|
mAllocator(allocator),
|
||||||
|
mContext(context),
|
||||||
|
mPool(std::make_unique<CommandBufferPool>(context, allocator, device, queue, queueFamilyIndex,
|
||||||
|
false)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanCommands::terminate() {
|
||||||
|
mPool.reset();
|
||||||
|
mProtectedPool.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanCommandBuffer& VulkanCommands::get() {
|
||||||
|
auto& ret = mPool->getRecording();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanCommandBuffer& VulkanCommands::getProtected() {
|
||||||
|
assert_invariant(mProtectedQueue != VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
if (!mProtectedPool) {
|
||||||
|
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mAllocator, mDevice,
|
||||||
|
mProtectedQueue, mProtectedQueueFamilyIndex, true);
|
||||||
|
}
|
||||||
|
auto& ret = mProtectedPool->getRecording();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanCommands::flush() {
|
||||||
|
VkSemaphore dependency = mInjectedDependency;
|
||||||
|
VkSemaphore lastSubmit = mLastSubmit;
|
||||||
|
bool hasFlushed = false;
|
||||||
|
|
||||||
|
// Note that we've ordered it so that the non-protected commands are followed by the protected
|
||||||
|
// commands. This assumes that the protected commands will be that one doing the rendering into
|
||||||
|
// the protected memory (i.e. protected render target).
|
||||||
|
for (auto pool: {mPool.get(), mProtectedPool.get()}) {
|
||||||
|
if (!pool || !pool->isRecording()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dependency != VK_NULL_HANDLE) {
|
||||||
|
pool->waitFor(dependency);
|
||||||
|
}
|
||||||
|
if (lastSubmit != VK_NULL_HANDLE) {
|
||||||
|
pool->waitFor(lastSubmit);
|
||||||
|
lastSubmit = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
dependency = pool->flush();
|
||||||
|
hasFlushed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasFlushed) {
|
||||||
|
mInjectedDependency = VK_NULL_HANDLE;
|
||||||
|
mLastSubmit = dependency;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSemaphore VulkanCommands::acquireFinishedSignal() {
|
|
||||||
VkSemaphore semaphore = mSubmissionSignal;
|
|
||||||
mSubmissionSignal = VK_NULL_HANDLE;
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
|
||||||
FVK_LOGI << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
|
|
||||||
#endif
|
|
||||||
return semaphore;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanCommands::injectDependency(VkSemaphore next) {
|
|
||||||
assert_invariant(mInjectedSignal == VK_NULL_HANDLE);
|
|
||||||
mInjectedSignal = next;
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
|
||||||
FVK_LOGI << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanCommands::wait() {
|
void VulkanCommands::wait() {
|
||||||
VkFence fences[CAPACITY];
|
FVK_SYSTRACE_CONTEXT();
|
||||||
size_t count = 0;
|
FVK_SYSTRACE_START("commands::wait");
|
||||||
for (size_t i = 0; i < CAPACITY; i++) {
|
|
||||||
auto wrapper = mStorage[i].get();
|
mPool->wait();
|
||||||
if (wrapper->buffer() != VK_NULL_HANDLE
|
if (mProtectedPool) {
|
||||||
&& mCurrentCommandBufferIndex != static_cast<int8_t>(i)) {
|
mProtectedPool->wait();
|
||||||
fences[count++] = wrapper->fence->getFence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
|
|
||||||
updateFences();
|
|
||||||
}
|
}
|
||||||
|
FVK_SYSTRACE_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommands::gc() {
|
void VulkanCommands::gc() {
|
||||||
FVK_SYSTRACE_CONTEXT();
|
FVK_SYSTRACE_CONTEXT();
|
||||||
FVK_SYSTRACE_START("commands::gc");
|
FVK_SYSTRACE_START("commands::gc");
|
||||||
|
|
||||||
VkFence fences[CAPACITY];
|
mPool->gc();
|
||||||
size_t count = 0;
|
if (mProtectedPool) {
|
||||||
|
mProtectedPool->gc();
|
||||||
for (size_t i = 0; i < CAPACITY; i++) {
|
|
||||||
auto wrapper = mStorage[i].get();
|
|
||||||
if (wrapper->buffer() == VK_NULL_HANDLE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto const vkfence = wrapper->fence->getFence();
|
|
||||||
VkResult const result = vkGetFenceStatus(mDevice, vkfence);
|
|
||||||
if (result != VK_SUCCESS) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fences[count++] = vkfence;
|
|
||||||
wrapper->fence->setValue(VK_SUCCESS);
|
|
||||||
wrapper->reset();
|
|
||||||
mAvailableBufferCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0) {
|
|
||||||
vkResetFences(mDevice, count, fences);
|
|
||||||
}
|
}
|
||||||
FVK_SYSTRACE_END();
|
FVK_SYSTRACE_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommands::updateFences() {
|
void VulkanCommands::updateFences() {
|
||||||
for (size_t i = 0; i < CAPACITY; i++) {
|
mPool->update();
|
||||||
auto wrapper = mStorage[i].get();
|
if (mProtectedPool) {
|
||||||
if (wrapper->buffer() != VK_NULL_HANDLE) {
|
mProtectedPool->update();
|
||||||
VulkanCmdFence* fence = wrapper->fence.get();
|
|
||||||
if (fence) {
|
|
||||||
VkResult status = vkGetFenceStatus(mDevice, fence->getFence());
|
|
||||||
// This is either VK_SUCCESS, VK_NOT_READY, or VK_ERROR_DEVICE_LOST.
|
|
||||||
fence->setValue(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,29 +477,14 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
|
|||||||
FVK_LOGD << "----> " << str << utils::io::endl;
|
FVK_LOGD << "----> " << str << utils::io::endl;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: Add group marker color to the Driver API
|
|
||||||
VkCommandBuffer const cmdbuffer = get().buffer();
|
|
||||||
|
|
||||||
if (!mGroupMarkers) {
|
if (!mGroupMarkers) {
|
||||||
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
|
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
|
||||||
}
|
}
|
||||||
mGroupMarkers->push(str, timestamp);
|
mGroupMarkers->push(str, timestamp);
|
||||||
|
|
||||||
if (mContext->isDebugUtilsSupported()) {
|
mPool->pushMarker(str);
|
||||||
VkDebugUtilsLabelEXT labelInfo = {
|
if (mProtectedPool) {
|
||||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
|
mProtectedPool->pushMarker(str);
|
||||||
.pLabelName = str,
|
|
||||||
.color = {0, 1, 0, 1},
|
|
||||||
};
|
|
||||||
vkCmdBeginDebugUtilsLabelEXT(cmdbuffer, &labelInfo);
|
|
||||||
} else if (mContext->isDebugMarkersSupported()) {
|
|
||||||
VkDebugMarkerMarkerInfoEXT markerInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
|
|
||||||
.pMarkerName = str,
|
|
||||||
.color = {0.0f, 1.0f, 0.0f, 1.0f},
|
|
||||||
};
|
|
||||||
vkCmdDebugMarkerBeginEXT(cmdbuffer, &markerInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,10 +502,9 @@ void VulkanCommands::popGroupMarker() {
|
|||||||
#else
|
#else
|
||||||
mGroupMarkers->pop();
|
mGroupMarkers->pop();
|
||||||
#endif
|
#endif
|
||||||
if (mContext->isDebugUtilsSupported()) {
|
mPool->popMarker();
|
||||||
vkCmdEndDebugUtilsLabelEXT(cmdbuffer);
|
if (mProtectedPool) {
|
||||||
} else if (mContext->isDebugMarkersSupported()) {
|
mProtectedPool->popMarker();
|
||||||
vkCmdDebugMarkerEndEXT(cmdbuffer);
|
|
||||||
}
|
}
|
||||||
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
|
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
|
||||||
// It could be that pop is called between flush() and get() (new command buffer), in which
|
// It could be that pop is called between flush() and get() (new command buffer), in which
|
||||||
@@ -455,22 +514,10 @@ void VulkanCommands::popGroupMarker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommands::insertEventMarker(char const* string, uint32_t len) {
|
void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
|
||||||
VkCommandBuffer const cmdbuffer = get().buffer();
|
mPool->insertEvent(str);
|
||||||
if (mContext->isDebugUtilsSupported()) {
|
if (mProtectedPool) {
|
||||||
VkDebugUtilsLabelEXT labelInfo = {
|
mProtectedPool->insertEvent(str);
|
||||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
|
|
||||||
.pLabelName = string,
|
|
||||||
.color = {1, 1, 0, 1},
|
|
||||||
};
|
|
||||||
vkCmdInsertDebugUtilsLabelEXT(cmdbuffer, &labelInfo);
|
|
||||||
} else if (mContext->isDebugMarkersSupported()) {
|
|
||||||
VkDebugMarkerMarkerInfoEXT markerInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
|
|
||||||
.pMarkerName = string,
|
|
||||||
.color = {0.0f, 1.0f, 0.0f, 1.0f},
|
|
||||||
};
|
|
||||||
vkCmdDebugMarkerInsertEXT(cmdbuffer, &markerInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "VulkanConstants.h"
|
#include "VulkanConstants.h"
|
||||||
#include "VulkanResources.h"
|
#include "VulkanResources.h"
|
||||||
|
#include "VulkanUtility.h"
|
||||||
|
|
||||||
#include <utils/Condition.h>
|
#include <utils/Condition.h>
|
||||||
#include <utils/FixedCapacityVector.h>
|
#include <utils/FixedCapacityVector.h>
|
||||||
@@ -61,43 +62,20 @@ private:
|
|||||||
|
|
||||||
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
|
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
|
||||||
struct VulkanCmdFence {
|
struct VulkanCmdFence {
|
||||||
struct SetValueScope {
|
VulkanCmdFence(VkResult initialStatus) {
|
||||||
public:
|
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||||
~SetValueScope() {
|
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||||
mHolder->mutex.unlock();
|
// finishes executing the command buffer, the status changes to VK_SUCCESS.
|
||||||
mHolder->condition.notify_all();
|
status.store(initialStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
SetValueScope(VulkanCmdFence* fenceHolder, VkResult result) :
|
|
||||||
mHolder(fenceHolder) {
|
|
||||||
mHolder->mutex.lock();
|
|
||||||
mHolder->status.store(result);
|
|
||||||
}
|
|
||||||
VulkanCmdFence* mHolder;
|
|
||||||
friend struct VulkanCmdFence;
|
|
||||||
};
|
|
||||||
|
|
||||||
VulkanCmdFence(VkFence ifence);
|
|
||||||
~VulkanCmdFence() = default;
|
~VulkanCmdFence() = default;
|
||||||
|
|
||||||
SetValueScope setValue(VkResult value) {
|
void setStatus(VkResult value) { status.store(value); }
|
||||||
return {this, value};
|
|
||||||
}
|
|
||||||
|
|
||||||
VkFence& getFence() {
|
VkResult getStatus() { return status.load(std::memory_order_acquire); }
|
||||||
return fence;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkResult getStatus() {
|
|
||||||
std::unique_lock<utils::Mutex> lock(mutex);
|
|
||||||
return status.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkFence fence;
|
|
||||||
utils::Condition condition;
|
|
||||||
utils::Mutex mutex;
|
|
||||||
std::atomic<VkResult> status;
|
std::atomic<VkResult> status;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,11 +83,14 @@ private:
|
|||||||
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
|
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
|
||||||
// we're done waiting on the most recent submission of the given command buffer.
|
// we're done waiting on the most recent submission of the given command buffer.
|
||||||
struct VulkanCommandBuffer {
|
struct VulkanCommandBuffer {
|
||||||
VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VkCommandPool pool);
|
VulkanCommandBuffer(VulkanContext* mContext, VulkanResourceAllocator* allocator,
|
||||||
|
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected);
|
||||||
|
|
||||||
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
|
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
|
||||||
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
|
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
|
||||||
|
|
||||||
|
~VulkanCommandBuffer();
|
||||||
|
|
||||||
inline void acquire(VulkanResource* resource) {
|
inline void acquire(VulkanResource* resource) {
|
||||||
mResourceManager.acquire(resource);
|
mResourceManager.acquire(resource);
|
||||||
}
|
}
|
||||||
@@ -117,41 +98,89 @@ struct VulkanCommandBuffer {
|
|||||||
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
|
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
|
||||||
mResourceManager.acquireAll(srcResources);
|
mResourceManager.acquireAll(srcResources);
|
||||||
}
|
}
|
||||||
|
void reset() noexcept;
|
||||||
|
|
||||||
inline void reset() {
|
inline void insertWait(VkSemaphore sem) {
|
||||||
fence.reset();
|
mWaitSemaphores.insert(sem);
|
||||||
mResourceManager.clear();
|
|
||||||
mPipeline = VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setPipeline(VkPipeline pipeline) {
|
void pushMarker(char const* marker) noexcept;
|
||||||
mPipeline = pipeline;
|
void popMarker() noexcept;
|
||||||
|
void insertEvent(char const* marker) noexcept;
|
||||||
|
|
||||||
|
void begin() noexcept;
|
||||||
|
VkSemaphore submit();
|
||||||
|
|
||||||
|
inline void setComplete() {
|
||||||
|
mFenceStatus->setStatus(VK_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VkPipeline pipeline() const {
|
VkResult getStatus() {
|
||||||
return mPipeline;
|
return mFenceStatus->getStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VkCommandBuffer buffer() const {
|
std::shared_ptr<VulkanCmdFence> getFenceStatus() const {
|
||||||
if (fence) {
|
return mFenceStatus;
|
||||||
return mBuffer;
|
|
||||||
}
|
|
||||||
return VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<VulkanCmdFence> fence;
|
VkFence getVkFence() const {
|
||||||
|
return mFence;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBuffer buffer() const {
|
||||||
|
FVK_LOGE <<"-- getting " << mBuffer << " -- ";
|
||||||
|
return mBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug only
|
||||||
|
bool mIsProtected;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
VulkanContext* mContext;
|
||||||
|
uint8_t mMarkerCount;
|
||||||
|
bool const isProtected;
|
||||||
|
VkDevice mDevice;
|
||||||
|
VkQueue mQueue;
|
||||||
VulkanAcquireOnlyResourceManager mResourceManager;
|
VulkanAcquireOnlyResourceManager mResourceManager;
|
||||||
|
CappedArray<VkSemaphore, 2> mWaitSemaphores;
|
||||||
VkCommandBuffer mBuffer;
|
VkCommandBuffer mBuffer;
|
||||||
VkPipeline mPipeline;
|
VkSemaphore mSubmission;
|
||||||
|
VkFence mFence;
|
||||||
|
std::shared_ptr<VulkanCmdFence> mFenceStatus;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allows classes to be notified after a new command buffer has been activated.
|
struct CommandBufferPool {
|
||||||
class CommandBufferObserver {
|
using ActiveBuffers = utils::bitset32;
|
||||||
public:
|
static constexpr int8_t INVALID = -1;
|
||||||
virtual void onCommandBuffer(const VulkanCommandBuffer& cmdbuffer) = 0;
|
|
||||||
virtual ~CommandBufferObserver();
|
CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, VkDevice device,
|
||||||
|
VkQueue queue, uint8_t queueFamilyIndex, bool isProtected);
|
||||||
|
~CommandBufferPool();
|
||||||
|
|
||||||
|
VulkanCommandBuffer& getRecording();
|
||||||
|
|
||||||
|
void gc();
|
||||||
|
void update();
|
||||||
|
VkSemaphore flush();
|
||||||
|
void wait();
|
||||||
|
|
||||||
|
void waitFor(VkSemaphore previousAction);
|
||||||
|
|
||||||
|
void pushMarker(char const* marker);
|
||||||
|
void popMarker();
|
||||||
|
void insertEvent(char const* marker);
|
||||||
|
|
||||||
|
inline bool isRecording() const { return mRecording != INVALID; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||||
|
using BufferList = utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>>;
|
||||||
|
VkDevice mDevice;
|
||||||
|
VkCommandPool mPool;
|
||||||
|
ActiveBuffers mSubmitted;
|
||||||
|
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
|
||||||
|
int8_t mRecording;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Manages a set of command buffers and semaphores, exposing an API that is significantly simpler
|
// Manages a set of command buffers and semaphores, exposing an API that is significantly simpler
|
||||||
@@ -165,9 +194,6 @@ public:
|
|||||||
// - This creates a guarantee of in-order execution.
|
// - This creates a guarantee of in-order execution.
|
||||||
// - Semaphores are recycled to prevent create / destroy churn.
|
// - Semaphores are recycled to prevent create / destroy churn.
|
||||||
//
|
//
|
||||||
// - Notifies listeners when recording begins in a new VkCommandBuffer.
|
|
||||||
// - Used by PipelineCache so that it knows when to clear out its shadow state.
|
|
||||||
//
|
|
||||||
// - Allows 1 user to inject a "dependency" semaphore that stalls the next flush.
|
// - Allows 1 user to inject a "dependency" semaphore that stalls the next flush.
|
||||||
// - This is used for asynchronous acquisition of a swap chain image, since the GPU
|
// - This is used for asynchronous acquisition of a swap chain image, since the GPU
|
||||||
// might require a valid swap chain image when it starts executing the command buffer.
|
// might require a valid swap chain image when it starts executing the command buffer.
|
||||||
@@ -183,13 +209,18 @@ public:
|
|||||||
class VulkanCommands {
|
class VulkanCommands {
|
||||||
public:
|
public:
|
||||||
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||||
VulkanContext* context, VulkanResourceAllocator* allocator);
|
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
|
||||||
|
VulkanResourceAllocator* allocator);
|
||||||
|
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
// Creates a "current" command buffer if none exists, otherwise returns the current one.
|
// Creates a "current" command buffer if none exists, otherwise returns the current one.
|
||||||
VulkanCommandBuffer& get();
|
VulkanCommandBuffer& get();
|
||||||
|
|
||||||
|
// Creates a "current" protected capable command buffer if none exists, otherwise
|
||||||
|
// returns the current one.
|
||||||
|
VulkanCommandBuffer& getProtected();
|
||||||
|
|
||||||
// Submits the current command buffer if it exists, then sets "current" to null.
|
// Submits the current command buffer if it exists, then sets "current" to null.
|
||||||
// If there are no outstanding commands then nothing happens and this returns false.
|
// If there are no outstanding commands then nothing happens and this returns false.
|
||||||
bool flush();
|
bool flush();
|
||||||
@@ -197,11 +228,17 @@ public:
|
|||||||
// Returns the "rendering finished" semaphore for the most recent flush and removes
|
// Returns the "rendering finished" semaphore for the most recent flush and removes
|
||||||
// it from the existing dependency chain. This is especially useful for setting up
|
// it from the existing dependency chain. This is especially useful for setting up
|
||||||
// vkQueuePresentKHR.
|
// vkQueuePresentKHR.
|
||||||
VkSemaphore acquireFinishedSignal();
|
VkSemaphore acquireFinishedSignal() {
|
||||||
|
VkSemaphore ret= mLastSubmit;
|
||||||
|
mLastSubmit = VK_NULL_HANDLE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
||||||
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
|
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
|
||||||
void injectDependency(VkSemaphore next);
|
void injectDependency(VkSemaphore next) {
|
||||||
|
mInjectedDependency = next;
|
||||||
|
}
|
||||||
|
|
||||||
// Destroys all command buffers that are no longer in use.
|
// Destroys all command buffers that are no longer in use.
|
||||||
void gc();
|
void gc();
|
||||||
@@ -212,37 +249,31 @@ public:
|
|||||||
// Updates the atomic "status" variable in every extant fence.
|
// Updates the atomic "status" variable in every extant fence.
|
||||||
void updateFences();
|
void updateFences();
|
||||||
|
|
||||||
// Sets an observer who is notified every time a new command buffer has been made "current".
|
|
||||||
// The observer's event handler can only be called during get().
|
|
||||||
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
|
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||||
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
|
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
|
||||||
|
|
||||||
void popGroupMarker();
|
void popGroupMarker();
|
||||||
|
|
||||||
void insertEventMarker(char const* string, uint32_t len);
|
void insertEventMarker(char const* string, uint32_t len);
|
||||||
|
|
||||||
std::string getTopGroupMarker() const;
|
std::string getTopGroupMarker() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||||
VkDevice const mDevice;
|
VkDevice const mDevice;
|
||||||
VkQueue const mQueue;
|
VkQueue const mProtectedQueue;
|
||||||
VkCommandPool const mPool;
|
// For defered initialization if/when we need protected content
|
||||||
VulkanContext const* mContext;
|
uint32_t const mProtectedQueueFamilyIndex;
|
||||||
|
VulkanResourceAllocator* mAllocator;
|
||||||
|
VulkanContext* mContext;
|
||||||
|
|
||||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||||
static_assert(CAPACITY < 128);
|
static_assert(CAPACITY < 128);
|
||||||
int8_t mCurrentCommandBufferIndex = -1;
|
|
||||||
VkSemaphore mSubmissionSignal = {};
|
std::unique_ptr<CommandBufferPool> mPool;
|
||||||
VkSemaphore mInjectedSignal = {};
|
std::unique_ptr<CommandBufferPool> mProtectedPool;
|
||||||
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
|
|
||||||
VkFence mFences[CAPACITY] = {};
|
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
|
||||||
VkSemaphore mSubmissionSignals[CAPACITY] = {};
|
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
|
||||||
uint8_t mAvailableBufferCount = CAPACITY;
|
|
||||||
CommandBufferObserver* mObserver = nullptr;
|
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
|
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_VALIDATION | FVK_DEBUG_DUMP_API)
|
||||||
#else
|
#else
|
||||||
#define FVK_DEBUG_FLAGS 0
|
#define FVK_DEBUG_FLAGS 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
|
|||||||
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
||||||
|
|
||||||
// We stash this because getResult might come before the query is actually processed.
|
// We stash this because getResult might come before the query is actually processed.
|
||||||
query->setFence(commands->fence);
|
query->setFence(commands->getFenceStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
|
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
|
||||||
|
|||||||
@@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
VK_DEFINE_HANDLE(VmaAllocator)
|
VK_DEFINE_HANDLE(VmaAllocator)
|
||||||
VK_DEFINE_HANDLE(VmaPool)
|
VK_DEFINE_HANDLE(VmaPool)
|
||||||
|
|
||||||
@@ -82,24 +85,53 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanRenderPass {
|
struct VulkanRenderPass {
|
||||||
|
VulkanCommandBuffer* cmdBuffer;
|
||||||
VulkanRenderTarget* renderTarget;
|
VulkanRenderTarget* renderTarget;
|
||||||
VkRenderPass renderPass;
|
VkRenderPass renderPass;
|
||||||
RenderPassParams params;
|
RenderPassParams params;
|
||||||
int currentSubpass;
|
int currentSubpass;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
std::string int_to_hex( T i )
|
||||||
|
{
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << "0x"
|
||||||
|
<< std::setfill ('0') << std::setw(sizeof(T)*2)
|
||||||
|
<< std::hex << i;
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool printed = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// This is a collection of immutable data about the vulkan context. This actual handles to the
|
// This is a collection of immutable data about the vulkan context. This actual handles to the
|
||||||
// context are stored in VulkanPlatform.
|
// context are stored in VulkanPlatform.
|
||||||
struct VulkanContext {
|
struct VulkanContext {
|
||||||
public:
|
public:
|
||||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
inline uint32_t selectMemoryType(uint32_t flags, VkMemoryPropertyFlags reqs) const {
|
||||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
||||||
if (flags & 1) {
|
assert_invariant(isProtectedMemorySupported()==true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!printed) {
|
||||||
|
for (uint32_t i = 0; i < mMemoryProperties.memoryTypeCount; i++) {
|
||||||
|
utils::slog.e << "[" << int_to_hex((uint8_t) i) << "]="
|
||||||
|
<< int_to_hex(
|
||||||
|
(uint32_t) mMemoryProperties.memoryTypes[i].propertyFlags)
|
||||||
|
<< utils::io::endl;
|
||||||
|
}
|
||||||
|
printed = true;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < mMemoryProperties.memoryTypeCount; i++) {
|
||||||
|
if (flags & (1ULL << i)) {
|
||||||
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flags >>= 1;
|
|
||||||
}
|
}
|
||||||
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
||||||
}
|
}
|
||||||
@@ -113,19 +145,19 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline VkPhysicalDeviceLimits const& getPhysicalDeviceLimits() const noexcept {
|
inline VkPhysicalDeviceLimits const& getPhysicalDeviceLimits() const noexcept {
|
||||||
return mPhysicalDeviceProperties.limits;
|
return mPhysicalDeviceProperties.properties.limits;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t getPhysicalDeviceVendorId() const noexcept {
|
inline uint32_t getPhysicalDeviceVendorId() const noexcept {
|
||||||
return mPhysicalDeviceProperties.vendorID;
|
return mPhysicalDeviceProperties.properties.vendorID;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isImageCubeArraySupported() const noexcept {
|
inline bool isImageCubeArraySupported() const noexcept {
|
||||||
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
|
return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isDepthClampSupported() const noexcept {
|
inline bool isDepthClampSupported() const noexcept {
|
||||||
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
|
return mPhysicalDeviceFeatures.features.depthClamp == VK_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isDebugMarkersSupported() const noexcept {
|
inline bool isDebugMarkersSupported() const noexcept {
|
||||||
@@ -141,21 +173,30 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool isClipDistanceSupported() const noexcept {
|
inline bool isClipDistanceSupported() const noexcept {
|
||||||
return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE;
|
return mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isLazilyAllocatedMemorySupported() const noexcept {
|
inline bool isLazilyAllocatedMemorySupported() const noexcept {
|
||||||
return mLazilyAllocatedMemorySupported;
|
return mLazilyAllocatedMemorySupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isProtectedMemorySupported() const noexcept {
|
||||||
|
return mProtectedMemorySupported;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
||||||
VkPhysicalDeviceProperties mPhysicalDeviceProperties = {};
|
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||||
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures = {};
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||||
|
};
|
||||||
|
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||||
|
};
|
||||||
bool mDebugMarkersSupported = false;
|
bool mDebugMarkersSupported = false;
|
||||||
bool mDebugUtilsSupported = false;
|
bool mDebugUtilsSupported = false;
|
||||||
bool mMultiviewEnabled = false;
|
bool mMultiviewEnabled = false;
|
||||||
bool mLazilyAllocatedMemorySupported = false;
|
bool mLazilyAllocatedMemorySupported = false;
|
||||||
|
bool mProtectedMemorySupported = false;
|
||||||
|
|
||||||
VkFormatList mDepthStencilFormats;
|
VkFormatList mDepthStencilFormats;
|
||||||
VkFormatList mBlittableDepthStencilFormats;
|
VkFormatList mBlittableDepthStencilFormats;
|
||||||
|
|||||||
@@ -198,8 +198,9 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
|||||||
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
|
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
|
||||||
mResourceManager(&mResourceAllocator),
|
mResourceManager(&mResourceAllocator),
|
||||||
mThreadSafeResourceManager(&mResourceAllocator),
|
mThreadSafeResourceManager(&mResourceAllocator),
|
||||||
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
|
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(),
|
||||||
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
|
mPlatform->getProtectedGraphicsQueue(), mPlatform->getProtectedGraphicsQueueFamilyIndex(),
|
||||||
|
&mContext, &mResourceAllocator),
|
||||||
mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator),
|
mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator),
|
||||||
mPipelineCache(mPlatform->getDevice(), mAllocator),
|
mPipelineCache(mPlatform->getDevice(), mAllocator),
|
||||||
mStagePool(mAllocator, &mCommands),
|
mStagePool(mAllocator, &mCommands),
|
||||||
@@ -365,6 +366,7 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
|||||||
FVK_SYSTRACE_START("beginFrame");
|
FVK_SYSTRACE_START("beginFrame");
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
FVK_SYSTRACE_END();
|
FVK_SYSTRACE_END();
|
||||||
|
FVK_LOGE <<"-----begin frame ---" << utils::io::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
||||||
@@ -384,6 +386,7 @@ void VulkanDriver::endFrame(uint32_t frameId) {
|
|||||||
mCommands.flush();
|
mCommands.flush();
|
||||||
collectGarbage();
|
collectGarbage();
|
||||||
FVK_SYSTRACE_END();
|
FVK_SYSTRACE_END();
|
||||||
|
FVK_LOGE <<"-----end frame ---" << utils::io::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::updateDescriptorSetBuffer(
|
void VulkanDriver::updateDescriptorSetBuffer(
|
||||||
@@ -526,19 +529,6 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
|||||||
FVK_SYSTRACE_END();
|
FVK_SYSTRACE_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
//void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
|
||||||
// TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
|
||||||
// TextureUsage usage,
|
|
||||||
// TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
|
|
||||||
// TextureSwizzle swizzleArray[] = {r, g, b, a};
|
|
||||||
// const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
|
|
||||||
// auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
|
|
||||||
// mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator,
|
|
||||||
// target, levels, format, samples, w, h, depth, usage, mStagePool,
|
|
||||||
// false /*heap allocated */, swizzleMap);
|
|
||||||
// mResourceManager.acquire(vktexture);
|
|
||||||
//}
|
|
||||||
|
|
||||||
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||||
uint8_t baseLevel, uint8_t levelCount) {
|
uint8_t baseLevel, uint8_t levelCount) {
|
||||||
VulkanTexture const* src = mResourceAllocator.handle_cast<VulkanTexture const*>(srch);
|
VulkanTexture const* src = mResourceAllocator.handle_cast<VulkanTexture const*>(srch);
|
||||||
@@ -690,8 +680,13 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||||
VulkanCommandBuffer const& commandBuffer = mCommands.get();
|
VulkanCommandBuffer* cmdbuf = nullptr;
|
||||||
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
|
if (mCurrentRenderPass.cmdBuffer) {
|
||||||
|
cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||||
|
} else {
|
||||||
|
cmdbuf = &mCommands.get();
|
||||||
|
}
|
||||||
|
mResourceAllocator.construct<VulkanFence>(fh, cmdbuf->getFenceStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||||
@@ -703,6 +698,15 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
|||||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||||
mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags);
|
mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags);
|
||||||
mResourceManager.acquire(swapChain);
|
mResourceManager.acquire(swapChain);
|
||||||
|
|
||||||
|
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||||
|
if (!isProtectedContentSupported()) {
|
||||||
|
utils::slog.w << "protected swapchain requested, but Vulkan does not support it"
|
||||||
|
<< utils::io::endl;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||||
@@ -977,8 +981,7 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanDriver::isProtectedContentSupported() {
|
bool VulkanDriver::isProtectedContentSupported() {
|
||||||
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
|
return mContext.isProtectedMemorySupported();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanDriver::isStereoSupported() {
|
bool VulkanDriver::isStereoSupported() {
|
||||||
@@ -1242,6 +1245,8 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
FVK_SYSTRACE_CONTEXT();
|
FVK_SYSTRACE_CONTEXT();
|
||||||
FVK_SYSTRACE_START("beginRenderPass");
|
FVK_SYSTRACE_START("beginRenderPass");
|
||||||
|
|
||||||
|
FVK_LOGE <<"begin render pass" << utils::io::endl;
|
||||||
|
|
||||||
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
|
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
|
||||||
VkExtent2D const& extent = rt->getExtent();
|
VkExtent2D const& extent = rt->getExtent();
|
||||||
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
|
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
|
||||||
@@ -1250,6 +1255,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
// first render pass. Note however that its contents are often preserved on subsequent render
|
// first render pass. Note however that its contents are often preserved on subsequent render
|
||||||
// passes, due to multiple views.
|
// passes, due to multiple views.
|
||||||
TargetBufferFlags discardStart = params.flags.discardStart;
|
TargetBufferFlags discardStart = params.flags.discardStart;
|
||||||
|
|
||||||
if (rt->isSwapChain()) {
|
if (rt->isSwapChain()) {
|
||||||
VulkanSwapChain* sc = mCurrentSwapChain;
|
VulkanSwapChain* sc = mCurrentSwapChain;
|
||||||
assert_invariant(sc);
|
assert_invariant(sc);
|
||||||
@@ -1270,7 +1276,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
|
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
|
||||||
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
|
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
|
||||||
// the non-sampling case.
|
// the non-sampling case.
|
||||||
VulkanCommandBuffer& commands = mCommands.get();
|
VulkanCommandBuffer& commands = rt->isProtected() ? mCommands.getProtected() : mCommands.get();
|
||||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||||
|
|
||||||
// Scissor is reset with each render pass
|
// Scissor is reset with each render pass
|
||||||
@@ -1379,6 +1385,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
|
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
|
||||||
|
|
||||||
mCurrentRenderPass = {
|
mCurrentRenderPass = {
|
||||||
|
.cmdBuffer = &commands,
|
||||||
.renderTarget = rt,
|
.renderTarget = rt,
|
||||||
.renderPass = renderPassInfo.renderPass,
|
.renderPass = renderPassInfo.renderPass,
|
||||||
.params = params,
|
.params = params,
|
||||||
@@ -1391,8 +1398,10 @@ void VulkanDriver::endRenderPass(int) {
|
|||||||
FVK_SYSTRACE_CONTEXT();
|
FVK_SYSTRACE_CONTEXT();
|
||||||
FVK_SYSTRACE_START("endRenderPass");
|
FVK_SYSTRACE_START("endRenderPass");
|
||||||
|
|
||||||
VulkanCommandBuffer& commands = mCommands.get();
|
FVK_LOGE <<"end render pass" << utils::io::endl;
|
||||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
|
||||||
|
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||||
|
VkCommandBuffer cmdbuffer = cmdbuf->buffer();
|
||||||
vkCmdEndRenderPass(cmdbuffer);
|
vkCmdEndRenderPass(cmdbuffer);
|
||||||
|
|
||||||
VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||||
@@ -1400,7 +1409,7 @@ void VulkanDriver::endRenderPass(int) {
|
|||||||
|
|
||||||
// Since we might soon be sampling from the render target that we just wrote to, we need a
|
// Since we might soon be sampling from the render target that we just wrote to, we need a
|
||||||
// pipeline barrier between framebuffer writes and shader reads.
|
// pipeline barrier between framebuffer writes and shader reads.
|
||||||
rt->emitBarriersEndRenderPass(commands);
|
rt->emitBarriersEndRenderPass(*cmdbuf);
|
||||||
|
|
||||||
mRenderPassFboInfo = {};
|
mRenderPassFboInfo = {};
|
||||||
mCurrentRenderPass.renderTarget = nullptr;
|
mCurrentRenderPass.renderTarget = nullptr;
|
||||||
@@ -1412,11 +1421,12 @@ void VulkanDriver::nextSubpass(int) {
|
|||||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0)
|
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0)
|
||||||
<< "Only two subpasses are currently supported.";
|
<< "Only two subpasses are currently supported.";
|
||||||
|
|
||||||
|
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||||
VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget;
|
VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget;
|
||||||
assert_invariant(renderTarget);
|
assert_invariant(renderTarget);
|
||||||
assert_invariant(mCurrentRenderPass.params.subpassMask);
|
assert_invariant(mCurrentRenderPass.params.subpassMask);
|
||||||
|
|
||||||
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdNextSubpass(cmdbuf->buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
|
||||||
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
|
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
|
||||||
++mCurrentRenderPass.currentSubpass);
|
++mCurrentRenderPass.currentSubpass);
|
||||||
@@ -1464,8 +1474,8 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
|||||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||||
backend::PushConstantVariant value) {
|
backend::PushConstantVariant value) {
|
||||||
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||||
VulkanCommands* commands = &mCommands;
|
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||||
mBoundPipeline.program->writePushConstant(commands, mBoundPipeline.pipelineLayout, stage, index,
|
mBoundPipeline.program->writePushConstant(cmdbuf, mBoundPipeline.pipelineLayout, stage, index,
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1476,6 +1486,7 @@ void VulkanDriver::insertEventMarker(char const* string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::pushGroupMarker(char const* string) {
|
void VulkanDriver::pushGroupMarker(char const* string) {
|
||||||
|
FVK_LOGE <<"---> " << string << utils::io::endl;
|
||||||
// Turns out all the markers are 0-terminated, so we can just pass it without len.
|
// Turns out all the markers are 0-terminated, so we can just pass it without len.
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||||
mCommands.pushGroupMarker(string);
|
mCommands.pushGroupMarker(string);
|
||||||
@@ -1654,9 +1665,9 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
|||||||
|
|
||||||
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||||
FVK_SYSTRACE_CONTEXT();
|
FVK_SYSTRACE_CONTEXT();
|
||||||
FVK_SYSTRACE_START("draw");
|
FVK_SYSTRACE_START("bindPipeline");
|
||||||
|
|
||||||
VulkanCommandBuffer* commands = &mCommands.get();
|
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||||
const VulkanVertexBufferInfo& vbi =
|
const VulkanVertexBufferInfo& vbi =
|
||||||
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
|
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
|
||||||
|
|
||||||
@@ -1667,6 +1678,8 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
|||||||
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
|
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
|
||||||
commands->acquire(program);
|
commands->acquire(program);
|
||||||
|
|
||||||
|
FVK_LOGE <<"bindpipeline with cmdbuf=" << commands->buffer() << utils::io::endl;
|
||||||
|
|
||||||
// Update the VK raster state.
|
// Update the VK raster state.
|
||||||
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||||
|
|
||||||
@@ -1738,7 +1751,7 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
|||||||
FVK_SYSTRACE_CONTEXT();
|
FVK_SYSTRACE_CONTEXT();
|
||||||
FVK_SYSTRACE_START("bindRenderPrimitive");
|
FVK_SYSTRACE_START("bindRenderPrimitive");
|
||||||
|
|
||||||
VulkanCommandBuffer* commands = &mCommands.get();
|
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||||
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||||
commands->acquire(prim.indexBuffer);
|
commands->acquire(prim.indexBuffer);
|
||||||
@@ -1775,10 +1788,12 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
|||||||
FVK_SYSTRACE_CONTEXT();
|
FVK_SYSTRACE_CONTEXT();
|
||||||
FVK_SYSTRACE_START("draw2");
|
FVK_SYSTRACE_START("draw2");
|
||||||
|
|
||||||
VulkanCommandBuffer& commands = mCommands.get();
|
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||||
|
|
||||||
mDescriptorSetManager.commit(&commands, mBoundPipeline.pipelineLayout,
|
FVK_LOGE <<"draw2 with cmdbuf=" << cmdbuffer << utils::io::endl;
|
||||||
|
|
||||||
|
mDescriptorSetManager.commit(commands, mBoundPipeline.pipelineLayout,
|
||||||
mBoundPipeline.descriptorSetMask);
|
mBoundPipeline.descriptorSetMask);
|
||||||
|
|
||||||
// Finally, make the actual draw call. TODO: support subranges
|
// Finally, make the actual draw call. TODO: support subranges
|
||||||
@@ -1806,8 +1821,8 @@ void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::scissor(Viewport scissorBox) {
|
void VulkanDriver::scissor(Viewport scissorBox) {
|
||||||
VulkanCommandBuffer& commands = mCommands.get();
|
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||||
|
|
||||||
// TODO: it's a common case that scissor() is called with (0, 0, maxint, maxint)
|
// TODO: it's a common case that scissor() is called with (0, 0, maxint, maxint)
|
||||||
// we should maybe have a fast path for this and avoid vkCmdSetScissor() if possible
|
// we should maybe have a fast path for this and avoid vkCmdSetScissor() if possible
|
||||||
|
|||||||
@@ -162,9 +162,8 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout,
|
void PushConstantDescription::write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout,
|
||||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||||
VulkanCommandBuffer* cmdbuf = &(commands->get());
|
|
||||||
uint32_t binaryValue = 0;
|
uint32_t binaryValue = 0;
|
||||||
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
|
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
|
||||||
if (std::holds_alternative<bool>(value)) {
|
if (std::holds_alternative<bool>(value)) {
|
||||||
@@ -267,7 +266,6 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
|||||||
VkExtent2D const extent = swapChain.getExtent();
|
VkExtent2D const extent = swapChain.getExtent();
|
||||||
width = extent.width;
|
width = extent.width;
|
||||||
height = extent.height;
|
height = extent.height;
|
||||||
|
|
||||||
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
|
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
|
||||||
mInfo->attachments = {color};
|
mInfo->attachments = {color};
|
||||||
|
|
||||||
@@ -293,6 +291,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
|||||||
fbkey.depth = depth.getImageView();
|
fbkey.depth = depth.getImageView();
|
||||||
}
|
}
|
||||||
mInfo->colors.set(0);
|
mInfo->colors.set(0);
|
||||||
|
mProtected = swapChain.isProtected();
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
|
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||||
@@ -303,6 +302,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
|||||||
: HwRenderTarget(width, height),
|
: HwRenderTarget(width, height),
|
||||||
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
||||||
mOffscreen(true),
|
mOffscreen(true),
|
||||||
|
mProtected(false),
|
||||||
mResources(handleAllocator),
|
mResources(handleAllocator),
|
||||||
mInfo(std::make_unique<Auxiliary>()) {
|
mInfo(std::make_unique<Auxiliary>()) {
|
||||||
|
|
||||||
@@ -335,6 +335,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mProtected = mProtected || texture->isProtected();
|
||||||
|
|
||||||
attachments.push_back(attachment);
|
attachments.push_back(attachment);
|
||||||
mInfo->colors.set(index);
|
mInfo->colors.set(index);
|
||||||
|
|
||||||
@@ -398,6 +400,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
|||||||
mResources.acquire(msaa);
|
mResources.acquire(msaa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mProtected = mProtected || depthTexture->isProtected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ struct PushConstantDescription {
|
|||||||
|
|
||||||
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
|
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
|
||||||
|
|
||||||
void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage,
|
void write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage,
|
||||||
uint8_t index, backend::PushConstantVariant const& value);
|
uint8_t index, backend::PushConstantVariant const& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -218,9 +218,9 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
|||||||
return mInfo->pushConstantDescription.getVkRanges();
|
return mInfo->pushConstantDescription.getVkRanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout,
|
inline void writePushConstant(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout,
|
||||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||||
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
|
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||||
@@ -307,6 +307,8 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
|||||||
|
|
||||||
inline bool isSwapChain() const { return !mOffscreen; }
|
inline bool isSwapChain() const { return !mOffscreen; }
|
||||||
|
|
||||||
|
bool isProtected() const { return mProtected; }
|
||||||
|
|
||||||
void bindToSwapChain(VulkanSwapChain& surf);
|
void bindToSwapChain(VulkanSwapChain& surf);
|
||||||
|
|
||||||
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
|
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
|
||||||
@@ -328,6 +330,7 @@ private:
|
|||||||
int8_t msaaIndex = UNDEFINED_INDEX;
|
int8_t msaaIndex = UNDEFINED_INDEX;
|
||||||
};
|
};
|
||||||
bool const mOffscreen;
|
bool const mOffscreen;
|
||||||
|
bool mProtected;
|
||||||
|
|
||||||
VulkanAcquireOnlyResourceManager mResources;
|
VulkanAcquireOnlyResourceManager mResources;
|
||||||
std::unique_ptr<Auxiliary> mInfo;
|
std::unique_ptr<Auxiliary> mInfo;
|
||||||
|
|||||||
@@ -66,17 +66,12 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
|
|||||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||||
|
|
||||||
PipelineCacheEntry* cacheEntry = getOrCreatePipeline();
|
PipelineCacheEntry* cacheEntry = getOrCreatePipeline();
|
||||||
// Check if the required pipeline is already bound.
|
|
||||||
if (cacheEntry->handle == commands->pipeline()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an error occurred, allow higher levels to handle it gracefully.
|
// If an error occurred, allow higher levels to handle it gracefully.
|
||||||
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
|
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
|
||||||
|
|
||||||
mBoundPipeline = mPipelineRequirements;
|
mBoundPipeline = mPipelineRequirements;
|
||||||
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
|
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
|
||||||
commands->setPipeline(cacheEntry->handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
|
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
|||||||
mTransitionSwapChainImageLayoutForPresent(
|
mTransitionSwapChainImageLayoutForPresent(
|
||||||
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
|
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
|
||||||
mAcquired(false),
|
mAcquired(false),
|
||||||
mIsFirstRenderPass(true) {
|
mIsFirstRenderPass(true),
|
||||||
|
mIsProtected(false) {
|
||||||
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
|
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
|
||||||
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
|
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
|
||||||
|
|
||||||
@@ -63,21 +64,28 @@ void VulkanSwapChain::update() {
|
|||||||
mColors.reserve(bundle.colors.size());
|
mColors.reserve(bundle.colors.size());
|
||||||
VkDevice const device = mPlatform->getDevice();
|
VkDevice const device = mPlatform->getDevice();
|
||||||
|
|
||||||
|
mIsProtected = bundle.protection;
|
||||||
|
TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT;
|
||||||
|
TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT;
|
||||||
|
if (bundle.protection) {
|
||||||
|
depthUsage |= TextureUsage::PROTECTED;
|
||||||
|
colorUsage |= TextureUsage::PROTECTED;
|
||||||
|
}
|
||||||
for (auto const color: bundle.colors) {
|
for (auto const color: bundle.colors) {
|
||||||
mColors.push_back(std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
|
mColors.push_back(std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
|
||||||
color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height,
|
color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height,
|
||||||
TextureUsage::COLOR_ATTACHMENT, mStagePool, true /* heap allocated */));
|
colorUsage, mStagePool, true /* heap allocated */));
|
||||||
}
|
}
|
||||||
mDepth = std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
|
mDepth = std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
|
||||||
bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height,
|
bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height,
|
||||||
TextureUsage::DEPTH_ATTACHMENT, mStagePool, true /* heap allocated */);
|
depthUsage, mStagePool, true /* heap allocated */);
|
||||||
|
|
||||||
mExtent = bundle.extent;
|
mExtent = bundle.extent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanSwapChain::present() {
|
void VulkanSwapChain::present() {
|
||||||
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
|
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
|
||||||
VulkanCommandBuffer& commands = mCommands->get();
|
VulkanCommandBuffer& commands = mIsProtected ? mCommands->getProtected() : mCommands->get();
|
||||||
VkImageSubresourceRange const subresources{
|
VkImageSubresourceRange const subresources{
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
.baseMipLevel = 0,
|
.baseMipLevel = 0,
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
|
|||||||
return mExtent;
|
return mExtent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isProtected() noexcept {
|
||||||
|
return mPlatform->isProtected(swapChain);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
|
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
|
||||||
|
|
||||||
@@ -97,6 +101,7 @@ private:
|
|||||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
||||||
bool mAcquired;
|
bool mAcquired;
|
||||||
bool mIsFirstRenderPass;
|
bool mIsFirstRenderPass;
|
||||||
|
bool mIsProtected;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -147,12 +147,14 @@ inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) {
|
|||||||
|
|
||||||
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
||||||
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
|
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
|
||||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout)
|
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
|
||||||
|
bool isProtected)
|
||||||
: VulkanResource(VulkanResourceType::HEAP_ALLOCATED),
|
: VulkanResource(VulkanResourceType::HEAP_ALLOCATED),
|
||||||
mVkFormat(format),
|
mVkFormat(format),
|
||||||
mViewType(viewType),
|
mViewType(viewType),
|
||||||
mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount},
|
mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount},
|
||||||
mDefaultLayout(defaultLayout),
|
mDefaultLayout(defaultLayout),
|
||||||
|
mIsProtected(isProtected),
|
||||||
mStagePool(stagePool),
|
mStagePool(stagePool),
|
||||||
mDevice(device),
|
mDevice(device),
|
||||||
mAllocator(allocator),
|
mAllocator(allocator),
|
||||||
@@ -183,7 +185,8 @@ VulkanTexture::VulkanTexture(
|
|||||||
mState(handleAllocator->initHandle<VulkanTextureState>(
|
mState(handleAllocator->initHandle<VulkanTextureState>(
|
||||||
device, allocator, commands, stagePool,
|
device, allocator, commands, stagePool,
|
||||||
format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1,
|
format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1,
|
||||||
getDefaultLayoutImpl(tusage))) {
|
getDefaultLayoutImpl(tusage),
|
||||||
|
any(usage& TextureUsage::PROTECTED))) {
|
||||||
auto* const state = getSharedState();
|
auto* const state = getSharedState();
|
||||||
state->mTextureImage = image;
|
state->mTextureImage = image;
|
||||||
mPrimaryViewRange = state->mFullViewRange;
|
mPrimaryViewRange = state->mFullViewRange;
|
||||||
@@ -201,7 +204,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
mAllocator(handleAllocator),
|
mAllocator(handleAllocator),
|
||||||
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
|
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
|
||||||
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
|
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
|
||||||
getLayerCount(target, depth), VulkanLayout::UNDEFINED)) {
|
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
|
||||||
auto* const state = getSharedState();
|
auto* const state = getSharedState();
|
||||||
|
|
||||||
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
|
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
|
||||||
@@ -232,6 +235,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
imageInfo.arrayLayers = depth * 6;
|
imageInfo.arrayLayers = depth * 6;
|
||||||
imageInfo.extent.depth = 1;
|
imageInfo.extent.depth = 1;
|
||||||
}
|
}
|
||||||
|
if (state->mIsProtected) {
|
||||||
|
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
@@ -331,7 +337,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
|
|
||||||
const VkFlags requiredMemoryFlags =
|
const VkFlags requiredMemoryFlags =
|
||||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||||
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U);
|
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
|
||||||
|
(state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||||
uint32_t memoryTypeIndex
|
uint32_t memoryTypeIndex
|
||||||
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
||||||
|
|
||||||
@@ -354,7 +361,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
// Go ahead and create the primary image view.
|
// Go ahead and create the primary image view.
|
||||||
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
|
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
|
||||||
|
|
||||||
VulkanCommandBuffer& commandsBuf = state->mCommands->get();
|
VulkanCommandBuffer& commandsBuf = state->mIsProtected ? state->mCommands->getProtected()
|
||||||
|
: state->mCommands->get();
|
||||||
|
|
||||||
commandsBuf.acquire(this);
|
commandsBuf.acquire(this);
|
||||||
|
|
||||||
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
||||||
@@ -416,6 +425,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
|||||||
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
|
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
|
||||||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
|
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
|
||||||
auto* const state = getSharedState();
|
auto* const state = getSharedState();
|
||||||
|
assert_invariant(!state->mIsProtected);
|
||||||
const PixelBufferDescriptor* hostData = &data;
|
const PixelBufferDescriptor* hostData = &data;
|
||||||
PixelBufferDescriptor reshapedData;
|
PixelBufferDescriptor reshapedData;
|
||||||
|
|
||||||
@@ -447,7 +457,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
|||||||
vmaUnmapMemory(state->mAllocator, stage->memory);
|
vmaUnmapMemory(state->mAllocator, stage->memory);
|
||||||
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
|
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
|
||||||
|
|
||||||
VulkanCommandBuffer& commands = state->mCommands->get();
|
VulkanCommandBuffer& commands =state->mCommands->get();
|
||||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||||
commands.acquire(this);
|
commands.acquire(this);
|
||||||
|
|
||||||
@@ -503,6 +513,8 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
|||||||
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
|
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
|
||||||
uint32_t height, uint32_t depth, uint32_t miplevel) {
|
uint32_t height, uint32_t depth, uint32_t miplevel) {
|
||||||
auto* const state = getSharedState();
|
auto* const state = getSharedState();
|
||||||
|
assert_invariant(!state->mIsProtected);
|
||||||
|
|
||||||
void* mapped = nullptr;
|
void* mapped = nullptr;
|
||||||
VulkanStageImage const* stage
|
VulkanStageImage const* stage
|
||||||
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
|
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ class VulkanResourceAllocator;
|
|||||||
struct VulkanTextureState : public VulkanResource {
|
struct VulkanTextureState : public VulkanResource {
|
||||||
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||||
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
||||||
uint8_t layerCount, VulkanLayout defaultLayout);
|
uint8_t layerCount, VulkanLayout defaultLayout,
|
||||||
|
bool isProtected = false);
|
||||||
|
|
||||||
struct ImageViewKey {
|
struct ImageViewKey {
|
||||||
VkImageSubresourceRange range; // 4 * 5 bytes
|
VkImageSubresourceRange range; // 4 * 5 bytes
|
||||||
@@ -70,6 +71,8 @@ struct VulkanTextureState : public VulkanResource {
|
|||||||
VkImage mTextureImage = VK_NULL_HANDLE;
|
VkImage mTextureImage = VK_NULL_HANDLE;
|
||||||
VulkanLayout mDefaultLayout;
|
VulkanLayout mDefaultLayout;
|
||||||
|
|
||||||
|
bool mIsProtected = false;
|
||||||
|
|
||||||
// Track the image layout of each subresource using a sparse range map.
|
// Track the image layout of each subresource using a sparse range map.
|
||||||
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
|
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
|
||||||
|
|
||||||
@@ -111,6 +114,13 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
|||||||
VulkanResourceAllocator* handleAllocator,
|
VulkanResourceAllocator* handleAllocator,
|
||||||
VulkanTexture const* src, VkComponentMapping swizzle);
|
VulkanTexture const* src, VkComponentMapping swizzle);
|
||||||
|
|
||||||
|
// Specialized constructor for external images
|
||||||
|
VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||||
|
VulkanResourceAllocator* handleAllocator,
|
||||||
|
VkImage image, VkDeviceMemory memory,
|
||||||
|
VkFormat format, uint32_t width, uint32_t height, TextureUsage tusage,
|
||||||
|
VulkanStagePool& stagePool);
|
||||||
|
|
||||||
~VulkanTexture();
|
~VulkanTexture();
|
||||||
|
|
||||||
// Uploads data into a subregion of a 2D or 3D texture.
|
// Uploads data into a subregion of a 2D or 3D texture.
|
||||||
@@ -179,6 +189,11 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
|||||||
return state->mIsTransientAttachment;
|
return state->mIsTransientAttachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isProtected() const {
|
||||||
|
VulkanTextureState const* state = getSharedState();
|
||||||
|
return state->mIsProtected;
|
||||||
|
}
|
||||||
|
|
||||||
bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range,
|
bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range,
|
||||||
VulkanLayout newLayout);
|
VulkanLayout newLayout);
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
|
|||||||
// Request all cross-platform extensions.
|
// Request all cross-platform extensions.
|
||||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||||
|
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
|
||||||
|
|
||||||
// Request these if available.
|
// Request these if available.
|
||||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||||
@@ -300,10 +301,11 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||||
VkPhysicalDeviceFeatures const& features, uint32_t graphicsQueueFamilyIndex,
|
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
|
||||||
ExtensionSet const& deviceExtensions) {
|
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const&
|
||||||
|
deviceExtensions) {
|
||||||
VkDevice device;
|
VkDevice device;
|
||||||
VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {};
|
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
|
||||||
const float queuePriority[] = {1.0f};
|
const float queuePriority[] = {1.0f};
|
||||||
VkDeviceCreateInfo deviceCreateInfo = {};
|
VkDeviceCreateInfo deviceCreateInfo = {};
|
||||||
FixedCapacityVector<const char*> requestExtensions;
|
FixedCapacityVector<const char*> requestExtensions;
|
||||||
@@ -314,22 +316,29 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
|||||||
for (auto const& ext: deviceExtensions) {
|
for (auto const& ext: deviceExtensions) {
|
||||||
requestExtensions.push_back(ext.data());
|
requestExtensions.push_back(ext.data());
|
||||||
}
|
}
|
||||||
deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
deviceQueueCreateInfo->queueFamilyIndex = graphicsQueueFamilyIndex;
|
deviceQueueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex;
|
||||||
deviceQueueCreateInfo->queueCount = 1;
|
deviceQueueCreateInfo[0].queueCount = 1;
|
||||||
deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0];
|
deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority[0];
|
||||||
|
// Protected queue
|
||||||
|
deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
|
||||||
|
deviceQueueCreateInfo[1].queueFamilyIndex = protectedGraphicsQueueFamilyIndex;
|
||||||
|
deviceQueueCreateInfo[1].queueCount = 1;
|
||||||
|
deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority[0];
|
||||||
|
|
||||||
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
deviceCreateInfo.queueCreateInfoCount = 1;
|
deviceCreateInfo.queueCreateInfoCount = protectedGraphicsQueueFamilyIndex == INVALID_VK_INDEX ? 1 : 2;
|
||||||
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
|
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
|
||||||
|
|
||||||
// We could simply enable all supported features, but since that may have performance
|
// We could simply enable all supported features, but since that may have performance
|
||||||
// consequences let's just enable the features we need.
|
// consequences let's just enable the features we need.
|
||||||
VkPhysicalDeviceFeatures enabledFeatures{
|
VkPhysicalDeviceFeatures enabledFeatures{
|
||||||
.depthClamp = features.depthClamp,
|
.depthClamp = features.features.depthClamp,
|
||||||
.samplerAnisotropy = features.samplerAnisotropy,
|
.samplerAnisotropy = features.features.samplerAnisotropy,
|
||||||
.textureCompressionETC2 = features.textureCompressionETC2,
|
.textureCompressionETC2 = features.features.textureCompressionETC2,
|
||||||
.textureCompressionBC = features.textureCompressionBC,
|
.textureCompressionBC = features.features.textureCompressionBC,
|
||||||
.shaderClipDistance = features.shaderClipDistance,
|
.shaderClipDistance = features.features.shaderClipDistance,
|
||||||
};
|
};
|
||||||
|
|
||||||
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
||||||
@@ -360,8 +369,20 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
|||||||
pNext = &multiview;
|
pNext = &multiview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
|
||||||
|
};
|
||||||
|
if (protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX) {
|
||||||
|
// Enable protected memory, if requested.
|
||||||
|
protectedMemory.protectedMemory = VK_TRUE;
|
||||||
|
|
||||||
|
protectedMemory.pNext = pNext;
|
||||||
|
pNext = &protectedMemory;
|
||||||
|
}
|
||||||
|
|
||||||
deviceCreateInfo.pNext = pNext;
|
deviceCreateInfo.pNext = pNext;
|
||||||
|
|
||||||
|
|
||||||
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
|
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateDevice error=" << result << ".";
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateDevice error=" << result << ".";
|
||||||
|
|
||||||
@@ -406,13 +427,13 @@ FixedCapacityVector<VkQueueFamilyProperties> getPhysicalDeviceQueueFamilyPropert
|
|||||||
return queueFamiliesProperties;
|
return queueFamiliesProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice) {
|
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags = VK_QUEUE_GRAPHICS_BIT) {
|
||||||
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties
|
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties
|
||||||
= getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice);
|
= getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice);
|
||||||
uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX;
|
uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX;
|
||||||
for (uint32_t j = 0; j < queueFamiliesProperties.size(); ++j) {
|
for (uint32_t j = 0; j < queueFamiliesProperties.size(); ++j) {
|
||||||
VkQueueFamilyProperties props = queueFamiliesProperties[j];
|
VkQueueFamilyProperties props = queueFamiliesProperties[j];
|
||||||
if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
if (props.queueCount != 0 && props.queueFlags & flags) {
|
||||||
graphicsQueueFamilyIndex = j;
|
graphicsQueueFamilyIndex = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -541,12 +562,16 @@ VkFormatList findAttachmentDepthStencilFormats(VkPhysicalDevice device) {
|
|||||||
|
|
||||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||||
VK_FORMAT_D24_UNORM_S8_UINT,
|
VK_FORMAT_D24_UNORM_S8_UINT,
|
||||||
|
|
||||||
|
// VK_FORMAT_D16_UNORM,
|
||||||
|
// VK_FORMAT_D16_UNORM_S8_UINT,
|
||||||
};
|
};
|
||||||
std::vector<VkFormat> selectedFormats;
|
std::vector<VkFormat> selectedFormats;
|
||||||
for (VkFormat format: formats) {
|
for (VkFormat format: formats) {
|
||||||
VkFormatProperties props;
|
VkFormatProperties props;
|
||||||
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
||||||
if ((props.optimalTilingFeatures & features) == features) {
|
if ((props.optimalTilingFeatures & features) == features ||
|
||||||
|
(props.linearTilingFeatures & features) == features) {
|
||||||
selectedFormats.push_back(format);
|
selectedFormats.push_back(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,6 +609,9 @@ struct VulkanPlatformPrivate {
|
|||||||
uint32_t mGraphicsQueueFamilyIndex = INVALID_VK_INDEX;
|
uint32_t mGraphicsQueueFamilyIndex = INVALID_VK_INDEX;
|
||||||
uint32_t mGraphicsQueueIndex = INVALID_VK_INDEX;
|
uint32_t mGraphicsQueueIndex = INVALID_VK_INDEX;
|
||||||
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
||||||
|
uint32_t mProtectedGraphicsQueueFamilyIndex = INVALID_VK_INDEX;
|
||||||
|
uint32_t mProtectedGraphicsQueueIndex = INVALID_VK_INDEX;
|
||||||
|
VkQueue mProtectedGraphicsQueue = VK_NULL_HANDLE;
|
||||||
VulkanContext mContext = {};
|
VulkanContext mContext = {};
|
||||||
|
|
||||||
// We use a map to both map a handle (i.e. SwapChainPtr) to the concrete type and also to
|
// We use a map to both map a handle (i.e. SwapChainPtr) to the concrete type and also to
|
||||||
@@ -686,10 +714,24 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
|
|
||||||
printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice);
|
printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice);
|
||||||
|
|
||||||
|
VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
|
||||||
|
};
|
||||||
|
// Chain to the pNext linked list
|
||||||
|
queryProtectedMemoryFeatures.pNext = context.mPhysicalDeviceFeatures.pNext;
|
||||||
|
context.mPhysicalDeviceFeatures.pNext = &queryProtectedMemoryFeatures;
|
||||||
|
|
||||||
|
|
||||||
|
VkPhysicalDeviceProtectedMemoryProperties protectedMemoryProperties = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
|
||||||
|
};
|
||||||
|
protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext;
|
||||||
|
context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties;
|
||||||
|
|
||||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||||
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
|
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
|
||||||
vkGetPhysicalDeviceProperties(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
|
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
|
||||||
vkGetPhysicalDeviceFeatures(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
|
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
|
||||||
vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties);
|
vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties);
|
||||||
|
|
||||||
mImpl->mGraphicsQueueFamilyIndex
|
mImpl->mGraphicsQueueFamilyIndex
|
||||||
@@ -698,10 +740,20 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
: mImpl->mGraphicsQueueFamilyIndex;
|
: mImpl->mGraphicsQueueFamilyIndex;
|
||||||
assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
|
assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
|
||||||
|
|
||||||
|
// We know we need to allocate the protected version of the VK objects
|
||||||
|
context.mProtectedMemorySupported = static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
|
||||||
|
if (context.mProtectedMemorySupported) {
|
||||||
|
mImpl->mProtectedGraphicsQueueFamilyIndex
|
||||||
|
= mImpl->mProtectedGraphicsQueueFamilyIndex == INVALID_VK_INDEX
|
||||||
|
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT))
|
||||||
|
: mImpl->mProtectedGraphicsQueueFamilyIndex;
|
||||||
|
assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
|
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
|
||||||
if (context.mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE
|
if (context.mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE
|
||||||
&& driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
|
&& driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
|
||||||
context.mPhysicalDeviceFeatures.shaderClipDistance = VK_FALSE;
|
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we should have a family index that points to a family that has > 0 queues for
|
// At this point, we should have a family index that points to a family that has > 0 queues for
|
||||||
@@ -711,6 +763,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
mImpl->mGraphicsQueueIndex
|
mImpl->mGraphicsQueueIndex
|
||||||
= mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex;
|
= mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex;
|
||||||
|
|
||||||
|
// Applying the same logic to the protected queue index (Not sure about shared context and protection)
|
||||||
|
mImpl->mProtectedGraphicsQueueIndex
|
||||||
|
= mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mProtectedGraphicsQueueIndex;
|
||||||
|
|
||||||
ExtensionSet deviceExts;
|
ExtensionSet deviceExts;
|
||||||
// If using a shared context, we do not assume any extensions.
|
// If using a shared context, we do not assume any extensions.
|
||||||
if (!mImpl->mSharedContext) {
|
if (!mImpl->mSharedContext) {
|
||||||
@@ -727,14 +783,23 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
|
|
||||||
mImpl->mDevice
|
mImpl->mDevice
|
||||||
= mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice,
|
= mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice,
|
||||||
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex, deviceExts)
|
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
|
||||||
: mImpl->mDevice;
|
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts) : mImpl->mDevice;
|
||||||
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
||||||
|
|
||||||
vkGetDeviceQueue(mImpl->mDevice, mImpl->mGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueIndex,
|
vkGetDeviceQueue(mImpl->mDevice, mImpl->mGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueIndex,
|
||||||
&mImpl->mGraphicsQueue);
|
&mImpl->mGraphicsQueue);
|
||||||
assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE);
|
assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
if (context.mProtectedMemorySupported) {
|
||||||
|
VkDeviceQueueInfo2 info = {};
|
||||||
|
info.queueFamilyIndex = mImpl->mProtectedGraphicsQueueFamilyIndex;
|
||||||
|
info.queueIndex = mImpl->mProtectedGraphicsQueueIndex;
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
|
||||||
|
info.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
|
||||||
|
vkGetDeviceQueue2(mImpl->mDevice, &info, &mImpl->mProtectedGraphicsQueue);
|
||||||
|
}
|
||||||
|
|
||||||
// Store the extension support in the context
|
// Store the extension support in the context
|
||||||
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||||
@@ -800,6 +865,10 @@ bool VulkanPlatform::hasResized(SwapChainPtr handle) {
|
|||||||
SWAPCHAIN_RET_FUNC(hasResized, handle, )
|
SWAPCHAIN_RET_FUNC(hasResized, handle, )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VulkanPlatform::isProtected(SwapChainPtr handle) {
|
||||||
|
SWAPCHAIN_RET_FUNC(isProtected, handle, )
|
||||||
|
}
|
||||||
|
|
||||||
VkResult VulkanPlatform::recreate(SwapChainPtr handle) {
|
VkResult VulkanPlatform::recreate(SwapChainPtr handle) {
|
||||||
SWAPCHAIN_RET_FUNC(recreate, handle, )
|
SWAPCHAIN_RET_FUNC(recreate, handle, )
|
||||||
}
|
}
|
||||||
@@ -835,6 +904,16 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
|
|||||||
mImpl->mPhysicalDevice, mImpl->mDevice, mImpl->mGraphicsQueue, mImpl->mInstance,
|
mImpl->mPhysicalDevice, mImpl->mDevice, mImpl->mGraphicsQueue, mImpl->mInstance,
|
||||||
surface, fallbackExtent, flags);
|
surface, fallbackExtent, flags);
|
||||||
mImpl->mSurfaceSwapChains.insert(swapchain);
|
mImpl->mSurfaceSwapChains.insert(swapchain);
|
||||||
|
|
||||||
|
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||||
|
if (!mImpl->mContext.mProtectedMemorySupported) {
|
||||||
|
utils::slog.w << "protected swapchain requested, but VulkanPlatform does not support it"
|
||||||
|
<< utils::io::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return swapchain;
|
return swapchain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,6 +941,18 @@ VkQueue VulkanPlatform::getGraphicsQueue() const noexcept {
|
|||||||
return mImpl->mGraphicsQueue;
|
return mImpl->mGraphicsQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t VulkanPlatform::getProtectedGraphicsQueueFamilyIndex() const noexcept {
|
||||||
|
return mImpl->mProtectedGraphicsQueueFamilyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VulkanPlatform::getProtectedGraphicsQueueIndex() const noexcept {
|
||||||
|
return mImpl->mProtectedGraphicsQueueIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
|
||||||
|
return mImpl->mProtectedGraphicsQueue;
|
||||||
|
}
|
||||||
|
|
||||||
#undef SWAPCHAIN_RET_FUNC
|
#undef SWAPCHAIN_RET_FUNC
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -195,15 +195,6 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#elif defined(WIN32)
|
#elif defined(WIN32)
|
||||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
|
||||||
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR()) does not correctly handle the fact that
|
|
||||||
// each native window has its own DPI_AWARENESS_CONTEXT, and erroneously uses the context
|
|
||||||
// of the calling thread. As a workaround, we set the current thread's DPI_AWARENESS_CONTEXT
|
|
||||||
// to that of the native window we've been given. This isn't a perfect solution, because an
|
|
||||||
// application could create swap chains on multiple native windows with varying DPI-awareness,
|
|
||||||
// but even then, at least one of the windows would be guaranteed to work correctly.
|
|
||||||
SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext((HWND) nativeWindow));
|
|
||||||
|
|
||||||
VkWin32SurfaceCreateInfoKHR const createInfo = {
|
VkWin32SurfaceCreateInfoKHR const createInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
|
||||||
.hinstance = GetModuleHandle(nullptr),
|
.hinstance = GetModuleHandle(nullptr),
|
||||||
|
|||||||
@@ -28,13 +28,96 @@ namespace filament::backend {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// std::tuple<VkImage, VkDeviceMemory> testing(VulkanContext const& context,
|
||||||
|
// VkDevice device, VkExtent2D extent) {
|
||||||
|
// // Filament expects blit() to work with any texture, so we almost always set these usage flags
|
||||||
|
// // (see copyFrame() and readPixels()).
|
||||||
|
// // The ordering here indicates the preference of choosing depth+stencil format.
|
||||||
|
// VkFormat const formats[] = {
|
||||||
|
// VK_FORMAT_D32_SFLOAT,
|
||||||
|
// VK_FORMAT_X8_D24_UNORM_PACK32,
|
||||||
|
|
||||||
|
// VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||||
|
// VK_FORMAT_D24_UNORM_S8_UINT,
|
||||||
|
// VK_FORMAT_D16_UNORM,
|
||||||
|
// VK_FORMAT_D16_UNORM_S8_UINT,
|
||||||
|
|
||||||
|
// VK_FORMAT_R8G8B8A8_SSCALED,
|
||||||
|
// VK_FORMAT_R8G8B8A8_UINT,
|
||||||
|
// VK_FORMAT_R8G8B8A8_SINT,
|
||||||
|
// VK_FORMAT_R8G8B8A8_SRGB,
|
||||||
|
// VK_FORMAT_B8G8R8A8_UNORM,
|
||||||
|
// VK_FORMAT_B8G8R8A8_SNORM,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// uint32_t memoryTypeIndex = VK_MAX_MEMORY_TYPES;
|
||||||
|
// for (auto tiling: {VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_TILING_LINEAR}) {
|
||||||
|
// for (auto format: formats) {
|
||||||
|
// VkImageCreateInfo imageInfo{
|
||||||
|
// .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||||
|
// .imageType = VK_IMAGE_TYPE_2D,
|
||||||
|
// .format = format,
|
||||||
|
// .extent = {extent.width, extent.height, 1},
|
||||||
|
// .mipLevels = 1,
|
||||||
|
// .arrayLayers = 1,
|
||||||
|
// .samples = VK_SAMPLE_COUNT_1_BIT,
|
||||||
|
// .tiling = tiling,
|
||||||
|
// .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||||||
|
// };
|
||||||
|
// VkImage image;
|
||||||
|
// VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
|
||||||
|
// FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
|
// << "Unable to create image: " << static_cast<int32_t>(result);
|
||||||
|
|
||||||
|
// // Allocate memory for the VkImage and bind it.
|
||||||
|
// // VkDeviceMemory imageMemory;
|
||||||
|
// VkMemoryRequirements memReqs;
|
||||||
|
// vkGetImageMemoryRequirements(device, image, &memReqs);
|
||||||
|
|
||||||
|
// utils::slog.e << "tiling=" << tiling << utils::io::endl;
|
||||||
|
// utils::slog.e << "choosing format=" << format << utils::io::endl;
|
||||||
|
// utils::slog.e << "required bits=" << memReqs.memoryTypeBits << utils::io::endl;
|
||||||
|
|
||||||
|
// memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits,
|
||||||
|
// VK_MEMORY_PROPERTY_PROTECTED_BIT);
|
||||||
|
// if (memoryTypeIndex != VK_MAX_MEMORY_TYPES) {
|
||||||
|
// utils::slog.e << "found!" << utils::io::endl;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// if (memoryTypeIndex != VK_MAX_MEMORY_TYPES) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||||
|
// // << "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
|
||||||
|
// // }
|
||||||
|
// // VkMemoryAllocateInfo allocInfo = {
|
||||||
|
// // .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||||
|
// // .allocationSize = memReqs.size,
|
||||||
|
// // .memoryTypeIndex = memoryTypeIndex,
|
||||||
|
// // };
|
||||||
|
// // result = vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory);
|
||||||
|
// // FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory.";
|
||||||
|
// // result = vkBindImageMemory(device, image, imageMemory, 0);
|
||||||
|
// // FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image.";
|
||||||
|
// return std::tuple(VK_NULL_HANDLE, VK_NULL_HANDLE);
|
||||||
|
// // return std::tuple(image, imageMemory);
|
||||||
|
// }
|
||||||
|
|
||||||
std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& context,
|
std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& context,
|
||||||
VkDevice device, VkExtent2D extent, VkFormat format) {
|
VkDevice device, VkExtent2D extent, VkFormat format, bool isProtected) {
|
||||||
bool const isDepth = isVkDepthFormat(format);
|
bool const isDepth = isVkDepthFormat(format);
|
||||||
|
// if (isDepth) {
|
||||||
|
// return testing(context, device, extent);
|
||||||
|
// }
|
||||||
|
|
||||||
// Filament expects blit() to work with any texture, so we almost always set these usage flags
|
// Filament expects blit() to work with any texture, so we almost always set these usage flags
|
||||||
// (see copyFrame() and readPixels()).
|
// (see copyFrame() and readPixels()).
|
||||||
VkImageUsageFlags const blittable =
|
VkImageUsageFlags const blittable =
|
||||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
(isProtected ? 0u
|
||||||
|
: (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
|
||||||
|
|
||||||
VkImageCreateInfo imageInfo {
|
VkImageCreateInfo imageInfo {
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||||
@@ -58,16 +141,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
|||||||
VkMemoryRequirements memReqs;
|
VkMemoryRequirements memReqs;
|
||||||
vkGetImageMemoryRequirements(device, image, &memReqs);
|
vkGetImageMemoryRequirements(device, image, &memReqs);
|
||||||
|
|
||||||
uint32_t memoryTypeIndex
|
// uint32_t const memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits,
|
||||||
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
// (isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
|
||||||
|
uint32_t const memoryTypeIndex =
|
||||||
|
context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
// (isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT :
|
||||||
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||||
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
|
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
|
||||||
|
|
||||||
VkMemoryAllocateInfo allocInfo = {
|
VkMemoryAllocateInfo allocInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||||
.allocationSize = memReqs.size,
|
.allocationSize = memReqs.size,
|
||||||
.memoryTypeIndex = memoryTypeIndex,
|
.memoryTypeIndex = memoryTypeIndex,
|
||||||
};
|
};
|
||||||
result = vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory);
|
result = vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory.";
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory.";
|
||||||
@@ -77,11 +163,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkFormat selectDepthFormat(VkFormatList const& depthFormats, bool hasStencil) {
|
VkFormat selectDepthFormat(VkFormatList const& depthFormats, bool hasStencil) {
|
||||||
|
for (auto f: depthFormats) {
|
||||||
|
utils::slog.e <<"formats=" << f << utils::io::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
auto const formatItr = std::find_if(depthFormats.begin(), depthFormats.end(),
|
auto const formatItr = std::find_if(depthFormats.begin(), depthFormats.end(),
|
||||||
hasStencil ? isVkStencilFormat : isVkDepthFormat);
|
hasStencil ? isVkStencilFormat : isVkDepthFormat);
|
||||||
assert_invariant(
|
assert_invariant(
|
||||||
formatItr != depthFormats.end() && "Cannot find suitable swapchain depth format");
|
formatItr != depthFormats.end() && "Cannot find suitable swapchain depth format");
|
||||||
return *formatItr;
|
*/
|
||||||
|
// return VK_FORMAT_D16_UNORM_S8_UINT;
|
||||||
|
return VK_FORMAT_D16_UNORM;
|
||||||
|
// return *formatItr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}// anonymous namespace
|
}// anonymous namespace
|
||||||
@@ -110,8 +204,9 @@ void VulkanPlatformSwapChainImpl::destroy() {
|
|||||||
mSwapChainBundle.colors.clear();
|
mSwapChainBundle.colors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format) {
|
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
|
||||||
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format);
|
bool isProtected) {
|
||||||
|
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected);
|
||||||
mMemory.insert({image, memory});
|
mMemory.insert({image, memory});
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
@@ -125,7 +220,8 @@ VulkanPlatformSurfaceSwapChain::VulkanPlatformSurfaceSwapChain(VulkanContext con
|
|||||||
mSurface(surface),
|
mSurface(surface),
|
||||||
mFallbackExtent(fallbackExtent),
|
mFallbackExtent(fallbackExtent),
|
||||||
mUsesRGB((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0),
|
mUsesRGB((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0),
|
||||||
mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0) {
|
mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0),
|
||||||
|
mIsProtected((flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) != 0) {
|
||||||
assert_invariant(surface);
|
assert_invariant(surface);
|
||||||
create();
|
create();
|
||||||
}
|
}
|
||||||
@@ -138,16 +234,32 @@ VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
|
|||||||
|
|
||||||
VkResult VulkanPlatformSurfaceSwapChain::create() {
|
VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||||
VkSurfaceFormatKHR surfaceFormat = {};
|
VkSurfaceFormatKHR surfaceFormat = {};
|
||||||
VkSurfaceCapabilitiesKHR caps;
|
VkSurfaceProtectedCapabilitiesKHR protectedCaps = {
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, mSurface, &caps);
|
.sType = VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR,
|
||||||
|
};
|
||||||
|
VkSurfaceCapabilities2KHR caps = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
|
||||||
|
.pNext = &protectedCaps,
|
||||||
|
};
|
||||||
|
VkPhysicalDeviceSurfaceInfo2KHR info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
|
||||||
|
.surface = mSurface,
|
||||||
|
};
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilities2KHR(mPhysicalDevice, &info, &caps);
|
||||||
|
|
||||||
|
FILAMENT_CHECK_PRECONDITION(!mIsProtected || protectedCaps.supportsProtected == VK_TRUE)
|
||||||
|
<< "Requesting protected swapchain but is not supported by the hardware";
|
||||||
|
|
||||||
|
utils::slog.e << "------ protected surface=" << (protectedCaps.supportsProtected == VK_TRUE)
|
||||||
|
<< utils::io::endl;
|
||||||
|
|
||||||
// The general advice is to require one more than the minimum swap chain length, since the
|
// The general advice is to require one more than the minimum swap chain length, since the
|
||||||
// absolute minimum could easily require waiting for a driver or presentation layer to release
|
// absolute minimum could easily require waiting for a driver or presentation layer to release
|
||||||
// the previous frame's buffer. The only situation in which we'd ask for the minimum length is
|
// the previous frame's buffer. The only situation in which we'd ask for the minimum length is
|
||||||
// when using a MAILBOX presentation strategy for low-latency situations where tearing is
|
// when using a MAILBOX presentation strategy for low-latency situations where tearing is
|
||||||
// acceptable.
|
// acceptable.
|
||||||
uint32_t const maxImageCount = caps.maxImageCount;
|
uint32_t const maxImageCount = caps.surfaceCapabilities.maxImageCount;
|
||||||
uint32_t const minImageCount = caps.minImageCount;
|
uint32_t const minImageCount = caps.surfaceCapabilities.minImageCount;
|
||||||
uint32_t desiredImageCount = minImageCount + 1;
|
uint32_t desiredImageCount = minImageCount + 1;
|
||||||
|
|
||||||
// According to section 30.5 of VK 1.1, maxImageCount of zero means "that there is no limit on
|
// According to section 30.5 of VK 1.1, maxImageCount of zero means "that there is no limit on
|
||||||
@@ -156,7 +268,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
|||||||
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
|
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
|
||||||
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
|
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
|
||||||
<< utils::io::endl;
|
<< utils::io::endl;
|
||||||
desiredImageCount = caps.minImageCount;
|
desiredImageCount = caps.surfaceCapabilities.minImageCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find a suitable surface format.
|
// Find a suitable surface format.
|
||||||
@@ -198,44 +310,50 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
|||||||
<< "Desired present mode is not supported by this device.";
|
<< "Desired present mode is not supported by this device.";
|
||||||
|
|
||||||
// Create the low-level swap chain.
|
// Create the low-level swap chain.
|
||||||
if (caps.currentExtent.width == VULKAN_UNDEFINED_EXTENT
|
auto const& currentExtent = caps.surfaceCapabilities.currentExtent;
|
||||||
|| caps.currentExtent.height == VULKAN_UNDEFINED_EXTENT) {
|
if (currentExtent.width == VULKAN_UNDEFINED_EXTENT
|
||||||
|
|| currentExtent.height == VULKAN_UNDEFINED_EXTENT) {
|
||||||
mSwapChainBundle.extent = mFallbackExtent;
|
mSwapChainBundle.extent = mFallbackExtent;
|
||||||
} else {
|
} else {
|
||||||
mSwapChainBundle.extent = caps.currentExtent;
|
mSwapChainBundle.extent = currentExtent;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkCompositeAlphaFlagBitsKHR const compositeAlpha
|
VkCompositeAlphaFlagBitsKHR const compositeAlpha =
|
||||||
= (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
|
(caps.surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
|
||||||
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
|
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
|
||||||
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
|
||||||
|
VkImageUsageFlags const transferBits =
|
||||||
|
(mIsProtected ? VkImageUsageFlagBits{}
|
||||||
|
: (VK_IMAGE_USAGE_TRANSFER_DST_BIT // Allows use as a blit destination
|
||||||
|
// (for copyFrame)
|
||||||
|
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT // Allows use as a blit source (for
|
||||||
|
// readPixels)
|
||||||
|
));
|
||||||
VkSwapchainCreateInfoKHR const createInfo{
|
VkSwapchainCreateInfoKHR const createInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||||
.surface = mSurface,
|
.flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : VkSwapchainCreateFlagsKHR{},
|
||||||
.minImageCount = desiredImageCount,
|
.surface = mSurface,
|
||||||
.imageFormat = surfaceFormat.format,
|
.minImageCount = desiredImageCount,
|
||||||
.imageColorSpace = surfaceFormat.colorSpace,
|
.imageFormat = surfaceFormat.format,
|
||||||
.imageExtent = mSwapChainBundle.extent,
|
.imageColorSpace = surfaceFormat.colorSpace,
|
||||||
.imageArrayLayers = 1,
|
.imageExtent = mSwapChainBundle.extent,
|
||||||
.imageUsage
|
.imageArrayLayers = 1,
|
||||||
= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transferBits,
|
||||||
| VK_IMAGE_USAGE_TRANSFER_DST_BIT // Allows use as a blit destination (for copyFrame)
|
|
||||||
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT,// Allows use as a blit source (for readPixels)
|
|
||||||
|
|
||||||
// TODO: Setting the preTransform to IDENTITY means we are letting the Android
|
// TODO: Setting the preTransform to IDENTITY means we are letting the
|
||||||
// Compositor handle the rotation. In some situations it might be more efficient to
|
// Android Compositor handle the rotation. In some situations it might be
|
||||||
// handle this ourselves by setting this field to be equal to the currentTransform mask
|
// more efficient to handle this ourselves by setting this field to be equal
|
||||||
// in the caps, but this would involve adjusting the MVP, derivatives in GLSL, and
|
// to the currentTransform mask in the caps, but this would involve
|
||||||
// possibly more.
|
// adjusting the MVP, derivatives in GLSL, and possibly more.
|
||||||
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
|
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
|
||||||
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||||
|
|
||||||
.compositeAlpha = compositeAlpha,
|
.compositeAlpha = compositeAlpha,
|
||||||
.presentMode = desiredPresentMode,
|
.presentMode = desiredPresentMode,
|
||||||
.clipped = VK_TRUE,
|
.clipped = VK_TRUE,
|
||||||
|
|
||||||
.oldSwapchain = mSwapchain,
|
.oldSwapchain = mSwapchain,
|
||||||
};
|
};
|
||||||
VkResult result = vkCreateSwapchainKHR(mDevice, &createInfo, VKALLOC, &mSwapchain);
|
VkResult result = vkCreateSwapchainKHR(mDevice, &createInfo, VKALLOC, &mSwapchain);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
@@ -245,13 +363,15 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
|||||||
mSwapChainBundle.colorFormat = surfaceFormat.format;
|
mSwapChainBundle.colorFormat = surfaceFormat.format;
|
||||||
mSwapChainBundle.depthFormat =
|
mSwapChainBundle.depthFormat =
|
||||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
|
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
|
||||||
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
|
mSwapChainBundle.depth =
|
||||||
|
createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat, mIsProtected);
|
||||||
|
mSwapChainBundle.protection = mIsProtected;
|
||||||
|
|
||||||
FVK_LOGI << "vkCreateSwapchain"
|
FVK_LOGI << "vkCreateSwapchain"
|
||||||
<< ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", "
|
<< ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", "
|
||||||
<< surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", "
|
<< surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", "
|
||||||
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
|
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
|
||||||
<< "identity-transform=" << (caps.currentTransform == 1) << ", "
|
<< "identity-transform=" << (caps.surfaceCapabilities.currentTransform == 1) << ", "
|
||||||
<< "depth=" << mSwapChainBundle.depthFormat
|
<< "depth=" << mSwapChainBundle.depthFormat
|
||||||
<< io::endl;
|
<< io::endl;
|
||||||
|
|
||||||
@@ -317,6 +437,10 @@ bool VulkanPlatformSurfaceSwapChain::hasResized() {
|
|||||||
return !equivalent(mSwapChainBundle.extent, perceivedExtent);
|
return !equivalent(mSwapChainBundle.extent, perceivedExtent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VulkanPlatformSurfaceSwapChain::isProtected() {
|
||||||
|
return mIsProtected;
|
||||||
|
}
|
||||||
|
|
||||||
// Non-virtual override
|
// Non-virtual override
|
||||||
VkResult VulkanPlatformSurfaceSwapChain::recreate() {
|
VkResult VulkanPlatformSurfaceSwapChain::recreate() {
|
||||||
destroy();
|
destroy();
|
||||||
@@ -338,6 +462,10 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
|
|||||||
VkDevice device, VkQueue queue, VkExtent2D extent, uint64_t flags)
|
VkDevice device, VkQueue queue, VkExtent2D extent, uint64_t flags)
|
||||||
: VulkanPlatformSwapChainImpl(context, device, queue),
|
: VulkanPlatformSwapChainImpl(context, device, queue),
|
||||||
mCurrentIndex(0) {
|
mCurrentIndex(0) {
|
||||||
|
|
||||||
|
assert_invariant(!(flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) &&
|
||||||
|
"Protected headless swapchain not supported");
|
||||||
|
|
||||||
mSwapChainBundle.extent = extent;
|
mSwapChainBundle.extent = extent;
|
||||||
mSwapChainBundle.colorFormat = (flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0
|
mSwapChainBundle.colorFormat = (flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0
|
||||||
? VK_FORMAT_R8G8B8A8_SRGB
|
? VK_FORMAT_R8G8B8A8_SRGB
|
||||||
@@ -347,13 +475,13 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
|
|||||||
images.reserve(HEADLESS_SWAPCHAIN_SIZE);
|
images.reserve(HEADLESS_SWAPCHAIN_SIZE);
|
||||||
images.resize(HEADLESS_SWAPCHAIN_SIZE);
|
images.resize(HEADLESS_SWAPCHAIN_SIZE);
|
||||||
for (size_t i = 0; i < HEADLESS_SWAPCHAIN_SIZE; ++i) {
|
for (size_t i = 0; i < HEADLESS_SWAPCHAIN_SIZE; ++i) {
|
||||||
images[i] = createImage(extent, mSwapChainBundle.colorFormat);
|
images[i] = createImage(extent, mSwapChainBundle.colorFormat, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
||||||
mSwapChainBundle.depthFormat =
|
mSwapChainBundle.depthFormat =
|
||||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
|
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
|
||||||
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat);
|
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() {
|
VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() {
|
||||||
|
|||||||
@@ -64,11 +64,16 @@ struct VulkanPlatformSwapChainImpl : public Platform::SwapChain {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Non-virtual override-able method
|
||||||
|
bool isProtected() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Non-virtual override-able method
|
// Non-virtual override-able method
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
VkImage createImage(VkExtent2D extent, VkFormat format);
|
VkImage createImage(VkExtent2D extent, VkFormat format, bool isProtected);
|
||||||
|
|
||||||
VulkanContext const& mContext;
|
VulkanContext const& mContext;
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
@@ -97,6 +102,9 @@ struct VulkanPlatformSurfaceSwapChain : public VulkanPlatformSwapChainImpl {
|
|||||||
// Non-virtual override-able method
|
// Non-virtual override-able method
|
||||||
bool hasResized();
|
bool hasResized();
|
||||||
|
|
||||||
|
// Non-virtual override-able method
|
||||||
|
bool isProtected();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Non-virtual override-able method
|
// Non-virtual override-able method
|
||||||
void destroy();
|
void destroy();
|
||||||
@@ -117,6 +125,7 @@ private:
|
|||||||
|
|
||||||
bool mUsesRGB = false;
|
bool mUsesRGB = false;
|
||||||
bool mHasStencil = false;
|
bool mHasStencil = false;
|
||||||
|
bool mIsProtected = false;
|
||||||
bool mSuboptimal;
|
bool mSuboptimal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -280,13 +280,14 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
|
|||||||
|
|
||||||
SYSTRACE_CALL();
|
SYSTRACE_CALL();
|
||||||
|
|
||||||
#if 0 && defined(__ANDROID__)
|
#if 1 && defined(__ANDROID__)
|
||||||
char scratch[PROP_VALUE_MAX + 1];
|
// char scratch[PROP_VALUE_MAX + 1];
|
||||||
int length = __system_property_get("debug.filament.protected", scratch);
|
// int length = __system_property_get("debug.filament.protected", scratch);
|
||||||
if (swapChain && length > 0) {
|
// if (swapChain && length > 0) {
|
||||||
|
if (swapChain) {
|
||||||
uint64_t flags = swapChain->getFlags();
|
uint64_t flags = swapChain->getFlags();
|
||||||
bool value = bool(atoi(scratch));
|
// bool value = bool(atoi(scratch));
|
||||||
if (value) {
|
if (true) {
|
||||||
flags |= SwapChain::CONFIG_PROTECTED_CONTENT;
|
flags |= SwapChain::CONFIG_PROTECTED_CONTENT;
|
||||||
} else {
|
} else {
|
||||||
flags &= ~SwapChain::CONFIG_PROTECTED_CONTENT;
|
flags &= ~SwapChain::CONFIG_PROTECTED_CONTENT;
|
||||||
|
|||||||
Reference in New Issue
Block a user