Compare commits

..
Author SHA1 Message Date
Powei Feng 8c7a82f7b3 vk: make protected content work 2024-10-31 11:17:19 -07:00
48 changed files with 953 additions and 1561 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.56.0'
implementation 'com.google.android.filament:filament-android:1.55.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.56.0'
pod 'Filament', '~> 1.55.0'
```
## Documentation
-4
View File
@@ -7,10 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.56.0
- backend: descriptor layouts distinguish samplers and external samplers (b/376089915) [⚠️ **New Material Version**]
## v1.55.1
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.56.0
VERSION_NAME=1.55.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
-7
View File
@@ -179,18 +179,11 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/caching/VulkanDescriptorSetManager.h
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
src/vulkan/caching/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
src/vulkan/memory/Resource.cpp
src/vulkan/memory/Resource.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/spirv/VulkanSpirvUtils.cpp
src/vulkan/spirv/VulkanSpirvUtils.h
src/vulkan/VulkanAsyncHandles.cpp
src/vulkan/VulkanAsyncHandles.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
src/vulkan/VulkanBuffer.cpp
@@ -227,7 +227,6 @@ enum class DescriptorType : uint8_t {
SHADER_STORAGE_BUFFER,
SAMPLER,
INPUT_ATTACHMENT,
SAMPLER_EXTERNAL
};
enum class DescriptorFlags : uint8_t {
@@ -88,7 +88,7 @@ public:
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
bool isProtected = false;
bool protection = false;
};
struct ImageSyncData {
-28
View File
@@ -1,28 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
#define TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
#include <utils/Systrace.h>
#define PROFILE_SCOPE(marker) SYSTRACE_NAME(marker)
#define PROFILE_NAME_BEGINFRAME "backend::beginFrame"
#define PROFILE_NAME_ENDFRAME "backend::endFrame"
#endif // TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
+11 -30
View File
@@ -623,8 +623,6 @@ const char* toString(DescriptorType type) {
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
@@ -821,24 +819,17 @@ void MetalDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void MetalDriver::destroySwapChain(Handle<HwSwapChain> sch) {
if (UTILS_UNLIKELY(!sch)) {
return;
}
auto* swapChain = handle_cast<MetalSwapChain>(sch);
if (mContext->currentDrawSwapChain == swapChain) {
mContext->currentDrawSwapChain = nullptr;
}
if (mContext->currentReadSwapChain == swapChain) {
mContext->currentReadSwapChain = nullptr;
}
// If the SwapChain is a pixel buffer, we need to wait for the current command buffer to
// complete before destroying it. This is because pixel buffer SwapChains hold a
// MetalExternalImage that could still being rendered into.
if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) {
executeAfterCurrentCommandBufferCompletes(
[this, sch]() mutable { destruct_handle<MetalSwapChain>(sch); });
} else {
destruct_handle<MetalSwapChain>(sch);
if (sch) {
auto* swapChain = handle_cast<MetalSwapChain>(sch);
// If the SwapChain is a pixel buffer, we need to wait for the current command buffer to
// complete before destroying it. This is because pixel buffer SwapChains hold a
// MetalExternalImage that could still being rendered into.
if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) {
executeAfterCurrentCommandBufferCompletes(
[this, sch]() mutable { destruct_handle<MetalSwapChain>(sch); });
} else {
destruct_handle<MetalSwapChain>(sch);
}
}
}
@@ -1803,16 +1794,6 @@ void MetalDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
if (UTILS_UNLIKELY(!dsh)) {
DEBUG_LOG("bindDescriptorSet(dsh = null, set = %d, offsets = [])\n", set);
mContext->currentDescriptorSets[set] = nullptr;
mContext->vertexDescriptorBindings.setBuffer(nil, 0, set);
mContext->fragmentDescriptorBindings.setBuffer(nil, 0, set);
mContext->dynamicOffsets.setOffsets(set, nullptr, 0);
return;
}
auto descriptorSet = handle_cast<MetalDescriptorSet>(dsh);
const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount();
utils::FixedCapacityVector<size_t> offsetsVector(dynamicBindings, 0);
+2 -4
View File
@@ -1290,8 +1290,7 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
[arguments addObject:bufferArgument];
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
textureArgument.index = binding.binding * 2;
textureArgument.dataType = MTLDataTypeTexture;
@@ -1406,8 +1405,7 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
atIndex:binding.binding * 2];
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
auto found = textures.find(binding.binding);
if (found == textures.end()) {
[encoder setTexture:driver->mContext->emptyTexture atIndex:binding.binding * 2];
+1 -3
View File
@@ -69,9 +69,7 @@ public:
assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT);
assert_invariant(binding < MAX_DESCRIPTOR_COUNT);
assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now
mStorage[set][binding] = { (uint8_t)entry.binding,
entry.type == DescriptorType::SAMPLER ||
entry.type == DescriptorType::SAMPLER_EXTERNAL };
mStorage[set][binding] = { (uint8_t)entry.binding, entry.type == DescriptorType::SAMPLER };
mActiveDescriptors[set].set(binding);
}
@@ -96,7 +96,6 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL:
if (UTILS_UNLIKELY(gl.isES2())) {
desc.emplace<SamplerGLES2>();
} else {
+8 -28
View File
@@ -23,7 +23,6 @@
#include "OpenGLDriverFactory.h"
#include "OpenGLProgram.h"
#include "OpenGLTimerQuery.h"
#include "SystraceProfile.h"
#include "gl_headers.h"
#include <backend/platforms/OpenGLPlatform.h>
@@ -92,35 +91,24 @@
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_GROUP_MARKER_ALL 0xFF // all markers
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (systrace)
#define DEBUG_MARKER_ALL (0xFF & ~DEBUG_MARKER_PROFILE) // all markers
#define DEBUG_MARKER_ALL 0x03 // all markers
// set to the desired debug marker level (for user markers [default: All])
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
// set to the desired debug level (for internal debugging [Default: None])
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE
# define DEBUG_MARKER()
# define PROFILE_MARKER(marker) PROFILE_SCOPE(marker);
# if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE
# error PROFILING is exclusive; group markers must be disabled.
# endif
#elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
# define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__);
# define PROFILE_MARKER(marker) DEBUG_MARKER()
# if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE
# error PROFILING is exclusive; all other debug features must be disabled.
# endif
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
# define DEBUG_MARKER() \
DebugMarker _debug_marker(*this, __func__);
#else
# define DEBUG_MARKER()
# define PROFILE_MARKER(marker)
#endif
using namespace filament::math;
@@ -3460,7 +3448,7 @@ void OpenGLDriver::beginFrame(
UTILS_UNUSED int64_t monotonic_clock_ns,
UTILS_UNUSED int64_t refreshIntervalNs,
UTILS_UNUSED uint32_t frameId) {
PROFILE_MARKER(PROFILE_NAME_BEGINFRAME)
DEBUG_MARKER()
auto& gl = mContext;
insertEventMarker("beginFrame");
mPlatform.beginFrame(monotonic_clock_ns, refreshIntervalNs, frameId);
@@ -3495,7 +3483,7 @@ void OpenGLDriver::setPresentationTime(int64_t monotonic_clock_ns) {
}
void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
PROFILE_MARKER(PROFILE_NAME_ENDFRAME)
DEBUG_MARKER()
#if defined(__EMSCRIPTEN__)
// WebGL builds are single-threaded so users might manipulate various GL state after we're
// done with the frame. We do NOT officially support using Filament in this way, but we can
@@ -3887,14 +3875,6 @@ void OpenGLDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
if (UTILS_UNLIKELY(!dsh)) {
mBoundDescriptorSets[set].dsh = dsh;
mInvalidDescriptorSetBindings.set(set, true);
mInvalidDescriptorSetBindingOffsets.set(set, true);
return;
}
// handle_cast<> here also serves to validate the handle (it actually cannot return nullptr)
GLDescriptorSet const* const ds = handle_cast<GLDescriptorSet*>(dsh);
if (ds) {
@@ -174,8 +174,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
}
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
if (!entry.name.empty()) {
GLint const loc = glGetUniformLocation(program, entry.name.c_str());
if (loc >= 0) {
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanAsyncHandles.h"
namespace filament::backend {
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
mStoppingQueryIndex(std::get<1>(indices)) {}
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
mFence = fence;
}
bool VulkanTimerQuery::isCompleted() noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
// QueryValue is a synchronous call and might occur before beginTimerQuery has written anything
// into the command buffer, which is an error according to the validation layer that ships in
// the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to require that the
// timestamp has at least been written into a processed command buffer.
// This fence indicates that the corresponding buffer has been completed.
return mFence && mFence->getStatus() == VK_SUCCESS;
}
VulkanTimerQuery::~VulkanTimerQuery() = default;
} // namespace filament::backend
@@ -1,109 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANASYNCHANDLES_H
#define TNT_FILAMENT_BACKEND_VULKANASYNCHANDLES_H
#include <bluevk/BlueVK.h>
#include "DriverBase.h"
#include "VulkanResources.h"
#include <utils/Mutex.h>
#include <utils/Condition.h>
namespace filament::backend {
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence {
struct SetValueScope {
public:
~SetValueScope() {
mHolder->mutex.unlock();
mHolder->condition.notify_all();
}
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;
SetValueScope setValue(VkResult value) {
return {this, value};
}
VkFence& getFence() {
return fence;
}
VkResult getStatus() {
std::unique_lock<utils::Mutex> lock(mutex);
return status.load(std::memory_order_acquire);
}
private:
VkFence fence;
utils::Condition condition;
utils::Mutex mutex;
std::atomic<VkResult> status;
};
struct VulkanFence : public HwFence, VulkanResource {
VulkanFence() : VulkanResource(VulkanResourceType::FENCE) {}
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
: VulkanResource(VulkanResourceType::FENCE),
fence(fence) {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
~VulkanTimerQuery();
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept;
bool isCompleted() noexcept;
uint32_t getStartingQueryIndex() const {
return mStartingQueryIndex;
}
uint32_t getStoppingQueryIndex() const {
return mStoppingQueryIndex;
}
private:
uint32_t mStartingQueryIndex;
uint32_t mStoppingQueryIndex;
std::shared_ptr<VulkanCmdFence> mFence;
utils::Mutex mFenceMutex;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANHASYNCANDLES_H
@@ -151,7 +151,8 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
}
#endif
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
mCommands->getProtected() : mCommands->get();
commands.acquire(src.texture);
commands.acquire(dst.texture);
resolveFast(&commands, aspect, src, dst);
@@ -176,7 +177,8 @@ void VulkanBlitter::blit(VkFilter filter,
#endif
// src and dst should have the same aspect here
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(dst.texture);
blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair);
+326 -279
View File
@@ -33,48 +33,28 @@ using namespace utils;
namespace filament::backend {
namespace {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
using Timestamp = VulkanGroupMarkers::Timestamp;
#endif
VulkanCmdFence::VulkanCmdFence(VkFence ifence)
: fence(ifence) {
// 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) {
VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
VkCommandBuffer cmdbuffer;
// Create the low-level command buffer.
const VkCommandBufferAllocateInfo allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
VkCommandBufferAllocateInfo const allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
// 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.
vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer);
vkAllocateCommandBuffers(device, &allocateInfo, &cmdbuffer);
return cmdbuffer;
}
CommandBufferObserver::~CommandBufferObserver() {}
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;
}
} // anonymous namespace
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
@@ -130,178 +110,147 @@ bool VulkanGroupMarkers::empty() const noexcept {
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator)
: mDevice(device),
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
mQueue(queue),
mPool(createPool(mDevice, queueFamilyIndex)),
mContext(context),
mStorage(CAPACITY) {
mResourceManager(allocator),
mBuffer(createCommandBuffer(device, pool)),
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
for (auto& semaphore: mSubmissionSignals) {
vkCreateSemaphore(mDevice, &sci, nullptr, &semaphore);
}
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
for (auto& fence: mFences) {
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
}
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
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
mIsProtected = isProtected;
}
void VulkanCommands::terminate() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
for (VkSemaphore sema: mSubmissionSignals) {
vkDestroySemaphore(mDevice, sema, VKALLOC);
VulkanCommandBuffer::~VulkanCommandBuffer() {
vkDestroySemaphore(mDevice, mSubmission, VKALLOC);
vkDestroyFence(mDevice, mFence, 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) {
vkDestroyFence(mDevice, fence, VKALLOC);
mMarkerCount++;
}
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() {
if (mCurrentCommandBufferIndex >= 0) {
return *mStorage[mCurrentCommandBufferIndex].get();
}
// 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]);
void VulkanCommandBuffer::begin() noexcept {
FVK_LOGE <<"[" << (mIsProtected ? "protected" : "regular") <<"]=" << buffer() << " begin"
<<utils::io::endl;
// Begin writing into the command buffer.
const VkCommandBufferBeginInfo binfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
VkCommandBufferBeginInfo const binfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(currentbuf->buffer(), &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;
vkBeginCommandBuffer(mBuffer, &binfo);
}
bool VulkanCommands::flush() {
// It's perfectly fine to call flush when no commands have been written.
if (mCurrentCommandBufferIndex < 0) {
return false;
VkSemaphore VulkanCommandBuffer::submit() {
while (mMarkerCount > 0) {
popMarker();
}
// Before actually submitting, we need to pop any leftover group markers.
// 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
vkEndCommandBuffer(mBuffer);
int8_t const index = mCurrentCommandBufferIndex;
VulkanCommandBuffer const* currentbuf = mStorage[index].get();
VkSemaphore const renderingFinished = mSubmissionSignals[index];
FVK_LOGE <<"[" << (mIsProtected ? "protected" : "regular") <<"]=" << buffer() << " end"
<<utils::io::endl;
vkEndCommandBuffer(currentbuf->buffer());
// If the injected semaphore is an "image available" semaphore that has not yet been signaled,
// 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,
VkPipelineStageFlags const 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{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = waitSemaphoreCount,
.pWaitSemaphores = waitSemaphoreCount > 0 ? signals : nullptr,
.pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1,
.pCommandBuffers = &cmdbuffer,
.signalSemaphoreCount = 1u,
.pSignalSemaphores = &renderingFinished,
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = mWaitSemaphores.size(),
.pWaitSemaphores = mWaitSemaphores.data(),
.pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1u,
.pCommandBuffers = &mBuffer,
.signalSemaphoreCount = 1u,
.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)
FVK_LOGI << "Submitting cmdbuffer=" << cmdbuffer
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
<< " signal=" << renderingFinished
<< " fence=" << currentbuf->fence->fence
<< utils::io::endl;
FVK_LOGI << "Submitting cmdbuffer=" << mBuffer
<< " wait=(";
for (size_t s = 0, count = mWaitSemaphores.size(); s < count; ++s) {
FVK_LOGI << mWaitSemaphores[s] << " ";
}
FVK_LOGI << ") "
<< " signal=" << mSubmission
<< " fence=" << mFence << utils::io::endl;
#endif
auto& cmdfence = currentbuf->fence;
UTILS_UNUSED_IN_RELEASE VkResult result = VK_SUCCESS;
{
auto scope = cmdfence->setValue(VK_NOT_READY);
result = vkQueueSubmit(mQueue, 1, &submitInfo, cmdfence->getFence());
}
mFenceStatus->setStatus(VK_NOT_READY);
VkResult result = vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
@@ -309,86 +258,212 @@ bool VulkanCommands::flush() {
}
#endif
assert_invariant(result == VK_SUCCESS);
mWaitSemaphores.clear();
return mSubmission;
}
mSubmissionSignal = renderingFinished;
mInjectedSignal = VK_NULL_HANDLE;
mCurrentCommandBufferIndex = -1;
CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected)
: 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;
}
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() {
VkFence fences[CAPACITY];
size_t count = 0;
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)) {
fences[count++] = wrapper->fence->getFence();
}
}
if (count > 0) {
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
updateFences();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commands::wait");
mPool->wait();
if (mProtectedPool) {
mProtectedPool->wait();
}
FVK_SYSTRACE_END();
}
void VulkanCommands::gc() {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commands::gc");
VkFence fences[CAPACITY];
size_t count = 0;
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);
mPool->gc();
if (mProtectedPool) {
mProtectedPool->gc();
}
FVK_SYSTRACE_END();
}
void VulkanCommands::updateFences() {
for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get();
if (wrapper->buffer() != VK_NULL_HANDLE) {
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);
}
}
mPool->update();
if (mProtectedPool) {
mProtectedPool->update();
}
}
@@ -402,29 +477,14 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
FVK_LOGD << "----> " << str << utils::io::endl;
}
#endif
// TODO: Add group marker color to the Driver API
VkCommandBuffer const cmdbuffer = get().buffer();
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(str, timestamp);
if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.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);
mPool->pushMarker(str);
if (mProtectedPool) {
mProtectedPool->pushMarker(str);
}
}
@@ -442,10 +502,9 @@ void VulkanCommands::popGroupMarker() {
#else
mGroupMarkers->pop();
#endif
if (mContext->isDebugUtilsSupported()) {
vkCmdEndDebugUtilsLabelEXT(cmdbuffer);
} else if (mContext->isDebugMarkersSupported()) {
vkCmdDebugMarkerEndEXT(cmdbuffer);
mPool->popMarker();
if (mProtectedPool) {
mProtectedPool->popMarker();
}
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
// 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) {
VkCommandBuffer const cmdbuffer = get().buffer();
if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = {
.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);
void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
mPool->insertEvent(str);
if (mProtectedPool) {
mProtectedPool->insertEvent(str);
}
}
+118 -46
View File
@@ -21,9 +21,9 @@
#include "DriverBase.h"
#include "VulkanAsyncHandles.h"
#include "VulkanConstants.h"
#include "VulkanResources.h"
#include "VulkanUtility.h"
#include <utils/Condition.h>
#include <utils/FixedCapacityVector.h>
@@ -60,15 +60,37 @@ private:
#endif // FVK_DEBUG_GROUP_MARKERS
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence {
VulkanCmdFence(VkResult initialStatus) {
// 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(initialStatus);
}
~VulkanCmdFence() = default;
void setStatus(VkResult value) { status.store(value); }
VkResult getStatus() { return status.load(std::memory_order_acquire); }
private:
std::atomic<VkResult> status;
};
// The submission fence has shared ownership semantics because it is potentially wrapped by a
// 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.
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& operator=(VulkanCommandBuffer const&) = delete;
~VulkanCommandBuffer();
inline void acquire(VulkanResource* resource) {
mResourceManager.acquire(resource);
}
@@ -76,41 +98,89 @@ struct VulkanCommandBuffer {
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
mResourceManager.acquireAll(srcResources);
}
void reset() noexcept;
inline void reset() {
fence.reset();
mResourceManager.clear();
mPipeline = VK_NULL_HANDLE;
inline void insertWait(VkSemaphore sem) {
mWaitSemaphores.insert(sem);
}
inline void setPipeline(VkPipeline pipeline) {
mPipeline = pipeline;
void pushMarker(char const* marker) noexcept;
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 {
return mPipeline;
VkResult getStatus() {
return mFenceStatus->getStatus();
}
inline VkCommandBuffer buffer() const {
if (fence) {
return mBuffer;
}
return VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> getFenceStatus() const {
return mFenceStatus;
}
std::shared_ptr<VulkanCmdFence> fence;
VkFence getVkFence() const {
return mFence;
}
VkCommandBuffer buffer() const {
FVK_LOGE <<"-- getting " << mBuffer << " -- ";
return mBuffer;
}
// debug only
bool mIsProtected;
private:
VulkanContext* mContext;
uint8_t mMarkerCount;
bool const isProtected;
VkDevice mDevice;
VkQueue mQueue;
VulkanAcquireOnlyResourceManager mResourceManager;
CappedArray<VkSemaphore, 2> mWaitSemaphores;
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.
class CommandBufferObserver {
public:
virtual void onCommandBuffer(const VulkanCommandBuffer& cmdbuffer) = 0;
virtual ~CommandBufferObserver();
struct CommandBufferPool {
using ActiveBuffers = utils::bitset32;
static constexpr int8_t INVALID = -1;
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
@@ -124,9 +194,6 @@ public:
// - This creates a guarantee of in-order execution.
// - 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.
// - 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.
@@ -142,13 +209,18 @@ public:
class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator);
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
VulkanResourceAllocator* allocator);
void terminate();
// Creates a "current" command buffer if none exists, otherwise returns the current one.
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.
// If there are no outstanding commands then nothing happens and this returns false.
bool flush();
@@ -156,11 +228,17 @@ public:
// 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
// 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
// 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.
void gc();
@@ -171,37 +249,31 @@ public:
// Updates the atomic "status" variable in every extant fence.
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)
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker();
void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const;
#endif
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice;
VkQueue const mQueue;
VkCommandPool const mPool;
VulkanContext const* mContext;
VkQueue const mProtectedQueue;
// For defered initialization if/when we need protected content
uint32_t const mProtectedQueueFamilyIndex;
VulkanResourceAllocator* mAllocator;
VulkanContext* mContext;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
int8_t mCurrentCommandBufferIndex = -1;
VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr;
std::unique_ptr<CommandBufferPool> mPool;
std::unique_ptr<CommandBufferPool> mProtectedPool;
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
+10 -31
View File
@@ -77,12 +77,8 @@
// order of calls).
#define FVK_DEBUG_FORCE_LOG_TO_I 0x00020000
// Enable a minimal set of traces to assess the performance of the backend.
// All other debug features must be disabled.
#define FVK_DEBUG_PROFILING 0x00040000
// Useful default combinations
#define FVK_DEBUG_EVERYTHING (0xFFFFFFFF & ~FVK_DEBUG_PROFILING)
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
FVK_DEBUG_SYSTRACE
@@ -93,7 +89,7 @@
#endif
#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
#define FVK_DEBUG_FLAGS 0
#endif
@@ -116,10 +112,6 @@ static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
#if FVK_ENABLED(FVK_DEBUG_PROFILING) && FVK_DEBUG_FLAGS != FVK_DEBUG_PROFILING
#error PROFILING is exclusive; all other debug features must be disabled.
#endif
// end dependcy checks
// Shorthand for combination of enabled debug flags
@@ -131,30 +123,17 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
// end shorthands
#if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#define FVK_SYSTRACE_SCOPE()
#define FVK_PROFILE_MARKER(marker) PROFILE_SCOPE(marker)
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#else
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#define FVK_SYSTRACE_SCOPE()
#define FVK_PROFILE_MARKER(marker)
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#endif
#ifndef FVK_HANDLE_ARENA_SIZE_IN_MB
@@ -86,7 +86,7 @@ VulkanTimestamps::VulkanTimestamps(VkDevice device) : mDevice(device) {
VkQueryPoolCreateInfo tqpCreateInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.queryType = VK_QUERY_TYPE_TIMESTAMP,
};
};
std::unique_lock<utils::Mutex> lock(mMutex);
tqpCreateInfo.queryCount = mUsed.size() * 2;
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
@@ -101,7 +101,7 @@ std::tuple<uint32_t, uint32_t> VulkanTimestamps::getNextQuery() {
for (size_t timerIndex = 0; timerIndex < maxTimers; ++timerIndex) {
if (!mUsed.test(timerIndex)) {
mUsed.set(timerIndex);
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
}
}
FVK_LOGE << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
@@ -121,7 +121,7 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
// 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,
+36 -7
View File
@@ -28,6 +28,9 @@
#include <memory>
#include <sstream>
#include <iomanip>
VK_DEFINE_HANDLE(VmaAllocator)
VK_DEFINE_HANDLE(VmaPool)
@@ -82,27 +85,53 @@ private:
};
struct VulkanRenderPass {
VulkanCommandBuffer* cmdBuffer;
VulkanRenderTarget* renderTarget;
VkRenderPass renderPass;
RenderPassParams params;
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
// context are stored in VulkanPlatform.
struct VulkanContext {
public:
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
inline uint32_t selectMemoryType(uint32_t flags, VkMemoryPropertyFlags reqs) const {
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported() == true);
assert_invariant(isProtectedMemorySupported()==true);
}
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
if (flags & 1) {
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) {
return i;
}
}
flags >>= 1;
}
return (uint32_t) VK_MAX_MEMORY_TYPES;
}
@@ -158,10 +187,10 @@ public:
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
};
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
+106 -58
View File
@@ -18,8 +18,6 @@
#include "CommandStreamDispatcher.h"
#include "DataReshaper.h"
#include "SystraceProfile.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
#include "VulkanCommands.h"
#include "VulkanDriverFactory.h"
@@ -106,7 +104,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
@@ -127,7 +125,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
}// anonymous namespace
@@ -157,7 +155,7 @@ DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Vulkan debug messenger.";
}
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
}
DebugUtils* DebugUtils::get() {
@@ -185,7 +183,7 @@ void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
};
vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info);
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
Dispatcher VulkanDriver::getDispatcher() const noexcept {
return ConcreteDispatcher<VulkanDriver>::make();
@@ -200,8 +198,9 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
mResourceManager(&mResourceAllocator),
mThreadSafeResourceManager(&mResourceAllocator),
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(),
mPlatform->getProtectedGraphicsQueue(), mPlatform->getProtectedGraphicsQueueFamilyIndex(),
&mContext, &mResourceAllocator),
mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator),
mPipelineCache(mPlatform->getDevice(), mAllocator),
mStagePool(mAllocator, &mCommands),
@@ -346,7 +345,8 @@ void VulkanDriver::tick(int) {
// rather than the wall clock, because we must wait 3 frames after a DriverAPI-level resource has
// been destroyed for safe destruction, due to outstanding command buffers and triple buffering.
void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("gc");
// Command buffers need to be submitted and completed before other resources can be gc'd. And
// its gc() function carrys out the *wait*.
mCommands.gc();
@@ -357,11 +357,16 @@ void VulkanDriver::collectGarbage() {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
mResourceAllocator.print();
#endif
FVK_SYSTRACE_END();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("beginFrame");
// Do nothing.
FVK_SYSTRACE_END();
FVK_LOGE <<"-----begin frame ---" << utils::io::endl;
}
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
@@ -376,9 +381,12 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
}
void VulkanDriver::endFrame(uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_ENDFRAME);
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endframe");
mCommands.flush();
collectGarbage();
FVK_SYSTRACE_END();
FVK_LOGE <<"-----end frame ---" << utils::io::endl;
}
void VulkanDriver::updateDescriptorSetBuffer(
@@ -411,17 +419,21 @@ void VulkanDriver::updateDescriptorSetTexture(
}
void VulkanDriver::flush(int) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("flush");
mCommands.flush();
FVK_SYSTRACE_END();
}
void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("finish");
mCommands.flush();
mCommands.wait();
mReadPixels.runUntilComplete();
FVK_SYSTRACE_END();
}
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
@@ -517,19 +529,6 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
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,
uint8_t baseLevel, uint8_t levelCount) {
VulkanTexture const* src = mResourceAllocator.handle_cast<VulkanTexture const*>(srch);
@@ -681,8 +680,13 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
VulkanCommandBuffer const& commandBuffer = mCommands.get();
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
VulkanCommandBuffer* cmdbuf = nullptr;
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) {
@@ -694,6 +698,15 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags);
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,
@@ -1229,7 +1242,10 @@ void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
}
void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("beginRenderPass");
FVK_LOGE <<"begin render pass" << utils::io::endl;
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
VkExtent2D const& extent = rt->getExtent();
@@ -1239,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
// passes, due to multiple views.
TargetBufferFlags discardStart = params.flags.discardStart;
if (rt->isSwapChain()) {
VulkanSwapChain* sc = mCurrentSwapChain;
assert_invariant(sc);
@@ -1259,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
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
// the non-sampling case.
VulkanCommandBuffer& commands = mCommands.get();
VulkanCommandBuffer& commands = rt->isProtected() ? mCommands.getProtected() : mCommands.get();
VkCommandBuffer const cmdbuffer = commands.buffer();
// Scissor is reset with each render pass
@@ -1368,18 +1385,23 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
mCurrentRenderPass = {
.cmdBuffer = &commands,
.renderTarget = rt,
.renderPass = renderPassInfo.renderPass,
.params = params,
.currentSubpass = 0,
};
FVK_SYSTRACE_END();
}
void VulkanDriver::endRenderPass(int) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endRenderPass");
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
FVK_LOGE <<"end render pass" << utils::io::endl;
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
VkCommandBuffer cmdbuffer = cmdbuf->buffer();
vkCmdEndRenderPass(cmdbuffer);
VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
@@ -1387,22 +1409,24 @@ void VulkanDriver::endRenderPass(int) {
// 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.
rt->emitBarriersEndRenderPass(commands);
rt->emitBarriersEndRenderPass(*cmdbuf);
mRenderPassFboInfo = {};
mCurrentRenderPass.renderTarget = nullptr;
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
FVK_SYSTRACE_END();
}
void VulkanDriver::nextSubpass(int) {
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0)
<< "Only two subpasses are currently supported.";
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget;
assert_invariant(renderTarget);
assert_invariant(mCurrentRenderPass.params.subpassMask);
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
vkCmdNextSubpass(cmdbuf->buffer(), VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
++mCurrentRenderPass.currentSubpass);
@@ -1414,7 +1438,8 @@ void VulkanDriver::nextSubpass(int) {
}
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
"Vulkan driver does not support distinct draw/read swap chains.");
@@ -1431,22 +1456,26 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
if (UTILS_LIKELY(mDefaultRenderTarget)) {
mDefaultRenderTarget->bindToSwapChain(*swapChain);
}
FVK_SYSTRACE_END();
}
void VulkanDriver::commit(Handle<HwSwapChain> sch) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commit");
VulkanSwapChain* swapChain = mResourceAllocator.handle_cast<VulkanSwapChain*>(sch);
// Present the backbuffer after the most recent command buffer submission has finished.
swapChain->present();
FVK_SYSTRACE_END();
}
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
VulkanCommands* commands = &mCommands;
mBoundPipeline.program->writePushConstant(commands, mBoundPipeline.pipelineLayout, stage, index,
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
mBoundPipeline.program->writePushConstant(cmdbuf, mBoundPipeline.pipelineLayout, stage, index,
value);
}
@@ -1457,6 +1486,7 @@ void VulkanDriver::insertEventMarker(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.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.pushGroupMarker(string);
@@ -1501,7 +1531,8 @@ void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
void VulkanDriver::resolve(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("resolve");
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "resolve() cannot be invoked inside a render pass.";
@@ -1537,13 +1568,16 @@ void VulkanDriver::resolve(
mBlitter.resolve(
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer },
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer });
FVK_SYSTRACE_END();
}
void VulkanDriver::blit(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
math::uint2 size) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blit");
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "blit() cannot be invoked inside a render pass.";
@@ -1577,13 +1611,16 @@ void VulkanDriver::blit(
mBlitter.blit(VK_FILTER_NEAREST,
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer }, dstOffsets,
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer }, srcOffsets);
FVK_SYSTRACE_END();
}
void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
SamplerMagFilter filter) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blitDEPRECATED");
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
@@ -1622,12 +1659,15 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
mBlitter.blit(vkfilter,
dstTarget->getColor0(), dstOffsets,
srcTarget->getColor0(), srcOffsets);
FVK_SYSTRACE_END();
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("bindPipeline");
VulkanCommandBuffer* commands = &mCommands.get();
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
const VulkanVertexBufferInfo& vbi =
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
@@ -1638,6 +1678,8 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
commands->acquire(program);
FVK_LOGE <<"bindpipeline with cmdbuf=" << commands->buffer() << utils::io::endl;
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
@@ -1701,12 +1743,15 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindPipeline(commands);
FVK_SYSTRACE_END();
}
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("bindRenderPrimitive");
VulkanCommandBuffer* commands = &mCommands.get();
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
commands->acquire(prim.indexBuffer);
@@ -1727,27 +1772,28 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
FVK_SYSTRACE_END();
}
void VulkanDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t setIndex,
backend::DescriptorSetOffsetArray&& offsets) {
if (dsh) {
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
} else {
mDescriptorSetManager.unbind(setIndex);
}
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
}
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw2");
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
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);
// Finally, make the actual draw call. TODO: support subranges
@@ -1756,6 +1802,8 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
@@ -1773,8 +1821,8 @@ void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
}
void VulkanDriver::scissor(Viewport scissorBox) {
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
VkCommandBuffer cmdbuffer = commands->buffer();
// 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
+28 -9
View File
@@ -91,10 +91,6 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
break;
}
case DescriptorType::SAMPLER_EXTERNAL: {
PANIC_POSTCONDITION("DescriptorType::SAMPLER_EXTERNAL is not supported yet");
break;
}
case DescriptorType::INPUT_ATTACHMENT: {
fromStageFlags(binding.stageFlags, binding.binding, mask.inputAttachment);
break;
@@ -166,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) {
VulkanCommandBuffer* cmdbuf = &(commands->get());
uint32_t binaryValue = 0;
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
if (std::holds_alternative<bool>(value)) {
@@ -260,7 +255,6 @@ VulkanRenderTarget::VulkanRenderTarget()
: HwRenderTarget(0, 0),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(false),
mProtected(false),
mResources(nullptr),
mInfo(std::make_unique<Auxiliary>()) {
mInfo->rpkey.samples = mInfo->fbkey.samples = 1;
@@ -272,8 +266,6 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
VkExtent2D const extent = swapChain.getExtent();
width = extent.width;
height = extent.height;
mProtected = swapChain.isProtected();
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
mInfo->attachments = {color};
@@ -299,6 +291,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
fbkey.depth = depth.getImageView();
}
mInfo->colors.set(0);
mProtected = swapChain.isProtected();
}
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
@@ -342,6 +335,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
continue;
}
mProtected = mProtected || texture->isProtected();
attachments.push_back(attachment);
mInfo->colors.set(index);
@@ -405,6 +400,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
mResources.acquire(msaa);
}
}
mProtected = mProtected || depthTexture->isProtected();
}
}
@@ -564,6 +560,29 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
mStoppingQueryIndex(std::get<1>(indices)) {}
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
mFence = fence;
}
bool VulkanTimerQuery::isCompleted() noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
// QueryValue is a synchronous call and might occur before beginTimerQuery has written anything
// into the command buffer, which is an error according to the validation layer that ships in
// the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to require that the
// timestamp has at least been written into a processed command buffer.
// This fence indicates that the corresponding buffer has been completed.
return mFence && mFence->getStatus() == VK_SUCCESS;
}
VulkanTimerQuery::~VulkanTimerQuery() = default;
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
+41 -4
View File
@@ -185,7 +185,7 @@ struct PushConstantDescription {
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);
private:
@@ -218,9 +218,9 @@ struct VulkanProgram : public HwProgram, VulkanResource {
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) {
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
@@ -306,7 +306,8 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
inline bool isSwapChain() const { return !mOffscreen; }
inline bool isProtected() const { return mProtected; }
bool isProtected() const { return mProtected; }
void bindToSwapChain(VulkanSwapChain& surf);
@@ -450,6 +451,42 @@ private:
FixedSizeVulkanResourceManager<2> mResources;
};
struct VulkanFence : public HwFence, VulkanResource {
VulkanFence()
: VulkanResource(VulkanResourceType::FENCE) {}
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
: VulkanResource(VulkanResourceType::FENCE),
fence(fence) {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
~VulkanTimerQuery();
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept;
bool isCompleted() noexcept;
uint32_t getStartingQueryIndex() const {
return mStartingQueryIndex;
}
uint32_t getStoppingQueryIndex() const {
return mStoppingQueryIndex;
}
private:
uint32_t mStartingQueryIndex;
uint32_t mStoppingQueryIndex;
std::shared_ptr<VulkanCmdFence> mFence;
utils::Mutex mFenceMutex;
};
inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
BufferObjectBinding bindingType) noexcept {
switch(bindingType) {
@@ -31,7 +31,6 @@
#pragma clang diagnostic ignored "-Wnullability-completeness"
#pragma clang diagnostic ignored "-Wextra-semi"
#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
static const PFN_vkGetInstanceProcAddr& vkGetInstanceProcAddr = bluevk::vkGetInstanceProcAddr;
static const PFN_vkGetDeviceProcAddr& vkGetDeviceProcAddr = bluevk::vkGetDeviceProcAddr;
@@ -66,17 +66,12 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
VkCommandBuffer const cmdbuffer = commands->buffer();
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.
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
mBoundPipeline = mPipelineRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
commands->setPipeline(cacheEntry->handle);
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
@@ -40,7 +40,8 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
mTransitionSwapChainImageLayoutForPresent(
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
mAcquired(false),
mIsFirstRenderPass(true) {
mIsFirstRenderPass(true),
mIsProtected(false) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
@@ -63,9 +64,10 @@ void VulkanSwapChain::update() {
mColors.reserve(bundle.colors.size());
VkDevice const device = mPlatform->getDevice();
mIsProtected = bundle.protection;
TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT;
TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT;
if (bundle.isProtected) {
if (bundle.protection) {
depthUsage |= TextureUsage::PROTECTED;
colorUsage |= TextureUsage::PROTECTED;
}
@@ -83,7 +85,7 @@ void VulkanSwapChain::update() {
void VulkanSwapChain::present() {
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mIsProtected ? mCommands->getProtected() : mCommands->get();
VkImageSubresourceRange const subresources{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
@@ -77,6 +77,7 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
inline bool isProtected() noexcept {
return mPlatform->isProtected(swapChain);
}
private:
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
@@ -100,6 +101,7 @@ private:
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired;
bool mIsFirstRenderPass;
bool mIsProtected;
};
@@ -204,7 +204,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
mAllocator(handleAllocator),
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) {
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
auto* const state = getSharedState();
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
@@ -235,7 +235,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.arrayLayers = depth * 6;
imageInfo.extent.depth = 1;
}
if (any(usage & TextureUsage::PROTECTED)) {
if (state->mIsProtected) {
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
}
@@ -361,7 +361,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
// Go ahead and create the primary image view.
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
VulkanCommandBuffer& commandsBuf = state->mCommands->get();
VulkanCommandBuffer& commandsBuf = state->mIsProtected ? state->mCommands->getProtected()
: state->mCommands->get();
commandsBuf.acquire(this);
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
@@ -423,7 +425,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
auto* const state = getSharedState();
assert_invariant(state->mIsProtected == false);
assert_invariant(!state->mIsProtected);
const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
@@ -455,7 +457,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
vmaUnmapMemory(state->mAllocator, stage->memory);
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
VulkanCommandBuffer& commands = state->mCommands->get();
VulkanCommandBuffer& commands =state->mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer();
commands.acquire(this);
@@ -511,6 +513,8 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
uint32_t height, uint32_t depth, uint32_t miplevel) {
auto* const state = getSharedState();
assert_invariant(!state->mIsProtected);
void* mapped = nullptr;
VulkanStageImage const* stage
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
+10 -2
View File
@@ -35,7 +35,8 @@ class VulkanResourceAllocator;
struct VulkanTextureState : public VulkanResource {
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
uint8_t layerCount, VulkanLayout defaultLayout,
bool isProtected = false);
struct ImageViewKey {
VkImageSubresourceRange range; // 4 * 5 bytes
@@ -113,6 +114,13 @@ struct VulkanTexture : public HwTexture, VulkanResource {
VulkanResourceAllocator* handleAllocator,
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();
// Uploads data into a subregion of a 2D or 3D texture.
@@ -181,7 +189,7 @@ struct VulkanTexture : public HwTexture, VulkanResource {
return state->mIsTransientAttachment;
}
bool getIsProtected() const {
bool isProtected() const {
VulkanTextureState const* state = getSharedState();
return state->mIsProtected;
}
@@ -369,10 +369,6 @@ void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = nullptr;
}
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
@@ -61,8 +61,6 @@ public:
void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets);
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
DescriptorSetMask const& setMask);
@@ -1,129 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/VulkanHandles.h"
namespace filament::backend::fvkmemory {
template ResourceType getTypeEnum<VulkanBufferObject>() noexcept;
template ResourceType getTypeEnum<VulkanIndexBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanProgram>() noexcept;
template ResourceType getTypeEnum<VulkanRenderTarget>() noexcept;
template ResourceType getTypeEnum<VulkanSwapChain>() noexcept;
template ResourceType getTypeEnum<VulkanRenderPrimitive>() noexcept;
template ResourceType getTypeEnum<VulkanTexture>() noexcept;
template ResourceType getTypeEnum<VulkanTextureState>() noexcept;
template ResourceType getTypeEnum<VulkanTimerQuery>() noexcept;
template ResourceType getTypeEnum<VulkanVertexBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanVertexBufferInfo>() noexcept;
template ResourceType getTypeEnum<VulkanDescriptorSetLayout>() noexcept;
template ResourceType getTypeEnum<VulkanDescriptorSet>() noexcept;
template ResourceType getTypeEnum<VulkanFence>() noexcept;
template<typename D>
ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanBufferObject>) {
return ResourceType::BUFFER_OBJECT;
}
if constexpr (std::is_same_v<D, VulkanIndexBuffer>) {
return ResourceType::INDEX_BUFFER;
}
if constexpr (std::is_same_v<D, VulkanProgram>) {
return ResourceType::PROGRAM;
}
if constexpr (std::is_same_v<D, VulkanRenderTarget>) {
return ResourceType::RENDER_TARGET;
}
if constexpr (std::is_same_v<D, VulkanSwapChain>) {
return ResourceType::SWAP_CHAIN;
}
if constexpr (std::is_same_v<D, VulkanRenderPrimitive>) {
return ResourceType::RENDER_PRIMITIVE;
}
if constexpr (std::is_same_v<D, VulkanTexture>) {
return ResourceType::TEXTURE;
}
if constexpr (std::is_same_v<D, VulkanTextureState>) {
return ResourceType::TEXTURE_STATE;
}
if constexpr (std::is_same_v<D, VulkanTimerQuery>) {
return ResourceType::TIMER_QUERY;
}
if constexpr (std::is_same_v<D, VulkanVertexBuffer>) {
return ResourceType::VERTEX_BUFFER;
}
if constexpr (std::is_same_v<D, VulkanVertexBufferInfo>) {
return ResourceType::VERTEX_BUFFER_INFO;
}
if constexpr (std::is_same_v<D, VulkanDescriptorSetLayout>) {
return ResourceType::DESCRIPTOR_SET_LAYOUT;
}
if constexpr (std::is_same_v<D, VulkanDescriptorSet>) {
return ResourceType::DESCRIPTOR_SET;
}
if constexpr (std::is_same_v<D, VulkanFence>) {
return ResourceType::FENCE;
}
return ResourceType::UNDEFINED_TYPE;
}
std::string getTypeStr(ResourceType type) {
switch (type) {
case ResourceType::BUFFER_OBJECT:
return "BufferObject";
case ResourceType::INDEX_BUFFER:
return "IndexBuffer";
case ResourceType::PROGRAM:
return "Program";
case ResourceType::RENDER_TARGET:
return "RenderTarget";
case ResourceType::SWAP_CHAIN:
return "SwapChain";
case ResourceType::RENDER_PRIMITIVE:
return "RenderPrimitive";
case ResourceType::TEXTURE:
return "Texture";
case ResourceType::TEXTURE_STATE:
return "TextureState";
case ResourceType::TIMER_QUERY:
return "TimerQuery";
case ResourceType::VERTEX_BUFFER:
return "VertexBuffer";
case ResourceType::VERTEX_BUFFER_INFO:
return "VertexBufferInfo";
case ResourceType::DESCRIPTOR_SET_LAYOUT:
return "DescriptorSetLayout";
case ResourceType::DESCRIPTOR_SET:
return "DescriptorSet";
case ResourceType::FENCE:
return "Fence";
case ResourceType::UNDEFINED_TYPE:
return "";
}
}
void Resource::destroy(ResourceType type, HandleId id) {
resManager->destructLaterWithType(type, id);
}
void ThreadSafeResource::destroy(ResourceType type, HandleId id) {
resManager->destructLaterWithType(type, id);
}
} // namespace filament::backend::fvkmemory
@@ -1,143 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
#include <private/backend/HandleAllocator.h>
#include <utils/Mutex.h>
#include <atomic>
#include <cstdint>
namespace filament::backend::fvkmemory {
using CounterIndex = int32_t;
using HandleId = HandleBase::HandleId;
class ResourceManager;
template <typename D>
struct resource_ptr;
// Subclasses of VulkanResource must provide this enum in their construction.
enum class ResourceType : uint8_t {
BUFFER_OBJECT = 0,
INDEX_BUFFER = 1,
PROGRAM = 2,
RENDER_TARGET = 3,
SWAP_CHAIN = 4,
RENDER_PRIMITIVE = 5,
TEXTURE = 6,
TEXTURE_STATE = 7,
TIMER_QUERY = 8,
VERTEX_BUFFER = 9,
VERTEX_BUFFER_INFO = 10,
DESCRIPTOR_SET_LAYOUT = 11,
DESCRIPTOR_SET = 12,
FENCE = 13,
UNDEFINED_TYPE = 14, // Must be the last enum because we use it for iterating over the enums.
};
template<typename D>
ResourceType getTypeEnum() noexcept;
std::string getTypeStr(ResourceType type);
inline bool isThreadSafeType(ResourceType type) {
return type == ResourceType::FENCE || type == ResourceType::TIMER_QUERY;
}
struct Resource {
Resource()
: resManager(nullptr),
id(HandleBase::nullid),
mCount(0),
restype(ResourceType::UNDEFINED_TYPE) {}
private:
inline void inc() noexcept {
mCount++;
}
inline void dec() noexcept {
assert_invariant(mCount > 0);
if (--mCount == 0) {
destroy(restype, id);
}
}
template <typename T>
inline void init(HandleId id, ResourceManager* resManager) {
this->id = id;
this->resManager = resManager;
this->restype = getTypeEnum<T>();
}
void destroy(ResourceType type, HandleId id);
ResourceManager* resManager; // 8
HandleId id; // 4
uint32_t mCount : 24;
ResourceType restype : 6; // restype + mCount is 4 bytes.
friend class ResourceManager;
template <typename D>
friend struct resource_ptr;
};
struct ThreadSafeResource {
ThreadSafeResource()
: resManager(nullptr),
id(HandleBase::nullid),
mCount(0),
restype(ResourceType::UNDEFINED_TYPE) {}
private:
inline void inc() noexcept {
mCount.fetch_add(1, std::memory_order_relaxed);
}
inline void dec() noexcept {
if (mCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
destroy(restype, id);
}
}
template <typename T>
inline void init(HandleId id, ResourceManager* resManager) {
this->id = id;
this->resManager = resManager;
this->restype = getTypeEnum<T>();
}
void destroy(ResourceType type, HandleId id);
ResourceManager* resManager; // 8
HandleId id; // 4
std::atomic<uint32_t> mCount; // 4
ResourceType restype; // 1
friend class ResourceManager;
template <typename D>
friend struct resource_ptr;
};
} // namespace filament::backend::fvkmemory
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
@@ -1,130 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/VulkanHandles.h"
#include <utils/Panic.h>
namespace filament::backend::fvkmemory {
namespace {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
uint32_t COUNTER[(size_t) ResourceType::UNDEFINED_TYPE] = {};
#endif
}
ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck)
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck) {}
void ResourceManager::gc() noexcept {
auto destroyAll = [this](GcList& list) {
for (auto const& [type, id]: list) {
destroyWithType(type, id);
}
list.clear();
};
{
// Note that we're not copying mThreadSafeGcList because the objects here do not have
// resource_ptrs to other handle objects, so their desctruction would not add more elements
// to mThreadSafeGcList.
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
destroyAll(mThreadSafeGcList);
}
GcList gcs;
gcs.insert(gcs.end(), mGcList.begin(), mGcList.end());
mGcList.clear();
destroyAll(gcs);
}
void ResourceManager::terminate() noexcept {
while (!mThreadSafeGcList.empty() || !mGcList.empty()) {
gc();
}
}
void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
switch (type) {
case ResourceType::BUFFER_OBJECT:
destruct<VulkanBufferObject>(Handle<VulkanBufferObject>(id));
break;
case ResourceType::INDEX_BUFFER:
destruct<VulkanIndexBuffer>(Handle<VulkanIndexBuffer>(id));
break;
case ResourceType::PROGRAM:
destruct<VulkanProgram>(Handle<VulkanProgram>(id));
break;
case ResourceType::RENDER_TARGET:
destruct<VulkanRenderTarget>(Handle<VulkanRenderTarget>(id));
break;
case ResourceType::SWAP_CHAIN:
destruct<VulkanSwapChain>(Handle<VulkanSwapChain>(id));
break;
case ResourceType::RENDER_PRIMITIVE:
destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
break;
case ResourceType::TEXTURE:
destruct<VulkanTexture>(Handle<VulkanTexture>(id));
break;
case ResourceType::TEXTURE_STATE:
destruct<VulkanTextureState>(Handle<VulkanTextureState>(id));
break;
case ResourceType::TIMER_QUERY:
destruct<VulkanTimerQuery>(Handle<VulkanTimerQuery>(id));
break;
case ResourceType::VERTEX_BUFFER:
destruct<VulkanVertexBuffer>(Handle<VulkanVertexBuffer>(id));
break;
case ResourceType::VERTEX_BUFFER_INFO:
destruct<VulkanVertexBufferInfo>(Handle<VulkanVertexBufferInfo>(id));
break;
case ResourceType::DESCRIPTOR_SET_LAYOUT:
destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
break;
case ResourceType::DESCRIPTOR_SET:
destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
break;
case ResourceType::FENCE:
destruct<VulkanFence>(Handle<VulkanFence>(id));
break;
case ResourceType::UNDEFINED_TYPE:
break;
}
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
COUNTER[(size_t) type]--;
#endif
}
void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
assert_invariant(type != ResourceType::UNDEFINED_TYPE);
COUNTER[(size_t) type]++;
#endif
}
void ResourceManager::print() const noexcept {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
utils::slog.e << "-------------------" << utils::io::endl;
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;
}
utils::slog.e << "+++++++++++++++++++" << utils::io::endl;
#endif
}
}
@@ -1,112 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
#include "vulkan/memory/Resource.h"
#include "vulkan/VulkanAsyncHandles.h"
#include <private/backend/HandleAllocator.h>
#include <utils/Panic.h>
namespace filament::backend::fvkmemory {
class ResourceManager {
public:
ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck);
template<typename D>
inline Handle<D> allocHandle() noexcept {
return mHandleAllocatorImpl.allocate<D>();
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
void gc() noexcept;
void print() const noexcept;
void terminate() noexcept;
private:
using AllocatorImpl = HandleAllocatorVK;
template<typename D>
using requires_thread_safety = typename std::disjunction<std::is_same<D, VulkanFence>,
std::is_same<D, VulkanTimerQuery>>;
template<typename D, typename B, typename... ARGS>
inline D* construct(Handle<B> const& handle, ARGS&&... args) noexcept {
constexpr bool THREAD_SAFETY = requires_thread_safety<D>::value;
D* obj = mHandleAllocatorImpl.construct<D, B>(handle, std::forward<ARGS>(args)...);
if constexpr (THREAD_SAFETY) {
((ThreadSafeResource*) obj)->template init<D>(handle.getId(), this);
} else {
((Resource*) obj)->template init<D>(handle.getId(), this);
}
traceConstruction(getTypeEnum<D>(), handle.getId());
return obj;
}
template<typename Dp, typename B>
inline typename std::enable_if_t<
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B> const& handle) noexcept {
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
}
template<typename D, typename B>
inline void destruct(Handle<B> handle) noexcept {
D* obj = handle_cast<D*>(handle);
mHandleAllocatorImpl.deallocate(handle, obj);
}
// This will post the destruction to a list that will be processed when gc is called from the
// backend thread.
inline void destructLaterWithType(ResourceType type, HandleId id) {
if (isThreadSafeType(type)) {
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
mThreadSafeGcList.push_back({type, id});
} else {
mGcList.push_back({type, id});
}
}
void destroyWithType(ResourceType type, HandleId id);
void traceConstruction(ResourceType type, HandleId id);
AllocatorImpl mHandleAllocatorImpl;
using GcList = std::vector<std::pair<ResourceType, HandleId>>;
utils::Mutex mThreadSafeGcListMutex;
GcList mThreadSafeGcList;
GcList mGcList;
template<typename D>
friend struct resource_ptr;
friend struct Resource;
friend struct ThreadSafeResource;
};
} // namespace filament::backend::fvkmemory
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
@@ -1,194 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourceManager.h"
#include <backend/Handle.h>
#include <utility>
namespace filament::backend {
class VulkanDriver;
namespace fvkmemory {
// This is a ref-counting, reference holder class (similar to std::shared_ptr) that serves as the
// primary interface between the vk backend (VulkanDriver) and the handle allocator. All objects
// that are allocated from the handle allocator are expected to be contained within a
template<typename D>
struct resource_ptr {
private:
template<typename B>
using enabled_resource_ptr =
resource_ptr<typename std::enable_if<std::is_base_of<B, D>::value, D>::type>;
public:
template<typename B, typename... ARGS>
static enabled_resource_ptr<B> make(ResourceManager* resManager, Handle<B> const& handle,
ARGS&&... args) noexcept {
D* ptr = resManager->construct<D, B>(handle, std::forward<ARGS>(args)...);
return {ptr};
}
// This will alloc a handle and then construct the object.
template<typename... ARGS>
static resource_ptr<D> construct(ResourceManager* resManager, ARGS&&... args) noexcept {
auto handle = resManager->allocHandle<D>();
D* ptr = resManager->construct<D, D>(handle, std::forward<ARGS>(args)...);
return {ptr};
}
template<typename B>
static enabled_resource_ptr<B> cast(ResourceManager* resManager,
Handle<B> const& handle) noexcept {
D* ptr = resManager->handle_cast<D*, B>(handle);
return {ptr};
}
template<typename B>
static enabled_resource_ptr<B> cast(B* ptr) noexcept {
return {ptr};
}
~resource_ptr() {
if (mRef) {
mRef->dec();
}
}
inline resource_ptr() = default;
// move constructor operator
inline resource_ptr(resource_ptr<D>&& rhs) {
(*this) = std::move(rhs);
}
inline resource_ptr(resource_ptr<D> const& rhs) {
(*this) = rhs;
}
template<typename E>
using is_supported_t =
typename std::enable_if<std::is_base_of<D, E>::value>::type;
template<typename E, typename = is_supported_t<E>>
inline resource_ptr(resource_ptr<E> const& rhs) {
(*this) = rhs;
}
// move operator
inline resource_ptr<D>& operator=(resource_ptr<D> && rhs) {
std::swap(mRef, rhs.mRef);
return *this;
}
inline resource_ptr<D>& operator=(resource_ptr<D> const& rhs) {
if (mRef == rhs.mRef) {
return *this;
}
if (mRef) {
mRef->dec();
}
mRef = rhs.mRef;
mRef->inc();
return *this;
}
template<typename E, typename = is_supported_t<E>>
inline resource_ptr<D>& operator=(resource_ptr<E> const& rhs) {
if (mRef == rhs.mRef) {
return *this;
}
if (mRef) {
mRef->dec();
}
mRef = rhs.mRef;
mRef->inc();
return *this;
}
inline bool operator==(resource_ptr<D> const& other) const {
return id() == other.id() && type() == other.type();
}
inline explicit operator bool() const {
return bool(mRef);
}
inline D* operator->() {
return get();
}
inline D const* operator->() const {
return get();
}
inline HandleId id() const {
if (mRef) {
return mRef->id;
}
return HandleBase::nullid;
}
inline D* get() const {
assert_invariant(mRef);
return mRef;
}
private:
// inc() and dec() For tracking ref-count with respect to create/destroy backend APIs. They can
// only be used from VulkanDriver.
inline void dec() {
assert_invariant(mRef);
mRef->dec();
}
inline void inc() {
assert_invariant(mRef);
mRef->inc();
}
inline ResourceType type() const {
if (mRef) {
return mRef->restype;
}
return ResourceType::UNDEFINED_TYPE;
}
resource_ptr(D* ref)
: mRef(ref) {
mRef->inc();
}
D* mRef = nullptr;
// Allow generic structures to hold resources of different types. For example,
// std::vector<fvkmemory::Resource> resources;
friend struct resource_ptr<Resource>;
friend struct resource_ptr<ThreadSafeResource>;
// This enables access to resource_ptr's private inc() and dec() methods.
friend class filament::backend::VulkanDriver;
};
} // namespace fvkmemory
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
@@ -159,6 +159,7 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
// Request all cross-platform extensions.
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
// Request these if available.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
@@ -301,7 +302,8 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions) {
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const&
deviceExtensions) {
VkDevice device;
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
const float queuePriority[] = {1.0f};
@@ -326,8 +328,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority[0];
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
bool const hasProtectedQueue = protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX;
deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1;
deviceCreateInfo.queueCreateInfoCount = protectedGraphicsQueueFamilyIndex == INVALID_VK_INDEX ? 1 : 2;
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
// We could simply enable all supported features, but since that may have performance
@@ -369,17 +370,19 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
}
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
};
if (hasProtectedQueue) {
if (protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX) {
// Enable protected memory, if requested.
protectedMemory.protectedMemory = VK_TRUE;
protectedMemory.pNext = pNext;
pNext = &protectedMemory;
}
deviceCreateInfo.pNext = pNext;
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateDevice error=" << result << ".";
@@ -424,7 +427,7 @@ FixedCapacityVector<VkQueueFamilyProperties> getPhysicalDeviceQueueFamilyPropert
return queueFamiliesProperties;
}
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags) {
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags = VK_QUEUE_GRAPHICS_BIT) {
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties
= getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice);
uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX;
@@ -491,8 +494,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
// Does the device have any command queues that support graphics?
// In theory, we should also ensure that the device supports presentation of our
// particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement.
if (identifyGraphicsQueueFamilyIndex(candidateDevice, VK_QUEUE_GRAPHICS_BIT)
== INVALID_VK_INDEX) {
if (identifyGraphicsQueueFamilyIndex(candidateDevice) == INVALID_VK_INDEX) {
continue;
}
@@ -560,12 +562,16 @@ VkFormatList findAttachmentDepthStencilFormats(VkPhysicalDevice device) {
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
// VK_FORMAT_D16_UNORM,
// VK_FORMAT_D16_UNORM_S8_UINT,
};
std::vector<VkFormat> selectedFormats;
for (VkFormat format: formats) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device, format, &props);
if ((props.optimalTilingFeatures & features) == features) {
if ((props.optimalTilingFeatures & features) == features ||
(props.linearTilingFeatures & features) == features) {
selectedFormats.push_back(format);
}
}
@@ -714,8 +720,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// 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,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
};
protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext;
context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties;
@@ -728,18 +736,17 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mGraphicsQueueFamilyIndex
= mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
VK_QUEUE_GRAPHICS_BIT)
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice)
: mImpl->mGraphicsQueueFamilyIndex;
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);
context.mProtectedMemorySupported = static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex
= identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
= 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);
}
@@ -758,8 +765,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// 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;
= mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mProtectedGraphicsQueueIndex;
ExtensionSet deviceExts;
// If using a shared context, we do not assume any extensions.
@@ -777,9 +783,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mDevice
= mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice,
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts)
: mImpl->mDevice;
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts) : mImpl->mDevice;
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
vkGetDeviceQueue(mImpl->mDevice, mImpl->mGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueIndex,
@@ -893,19 +898,22 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
flags |= SWAP_CHAIN_CONFIG_ENABLE_XCB;
}
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;
}
}
auto [surface, fallbackExtent] = createVkSurfaceKHR(nativeWindow, mImpl->mInstance, flags);
// The VulkanPlatformSurfaceSwapChain now `owns` the surface.
VulkanPlatformSurfaceSwapChain* swapchain = new VulkanPlatformSurfaceSwapChain(mImpl->mContext,
mImpl->mPhysicalDevice, mImpl->mDevice, mImpl->mGraphicsQueue, mImpl->mInstance,
surface, fallbackExtent, flags);
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;
}
@@ -195,15 +195,6 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
}
#endif
#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 = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.hinstance = GetModuleHandle(nullptr),
@@ -28,13 +28,96 @@ namespace filament::backend {
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,
VkDevice device, VkExtent2D extent, VkFormat format) {
VkDevice device, VkExtent2D extent, VkFormat format, bool isProtected) {
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
// (see copyFrame() and readPixels()).
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 {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -58,16 +141,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device, image, &memReqs);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
// uint32_t const memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits,
// (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)
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex,
.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.";
@@ -77,11 +163,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
}
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(),
hasStencil ? isVkStencilFormat : isVkDepthFormat);
assert_invariant(
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
@@ -110,8 +204,9 @@ void VulkanPlatformSwapChainImpl::destroy() {
mSwapChainBundle.colors.clear();
}
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format) {
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format);
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
bool isProtected) {
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected);
mMemory.insert({image, memory});
return image;
}
@@ -139,16 +234,32 @@ VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
VkResult VulkanPlatformSurfaceSwapChain::create() {
VkSurfaceFormatKHR surfaceFormat = {};
VkSurfaceCapabilitiesKHR caps;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, mSurface, &caps);
VkSurfaceProtectedCapabilitiesKHR protectedCaps = {
.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
// 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
// when using a MAILBOX presentation strategy for low-latency situations where tearing is
// acceptable.
uint32_t const maxImageCount = caps.maxImageCount;
uint32_t const minImageCount = caps.minImageCount;
uint32_t const maxImageCount = caps.surfaceCapabilities.maxImageCount;
uint32_t const minImageCount = caps.surfaceCapabilities.minImageCount;
uint32_t desiredImageCount = minImageCount + 1;
// According to section 30.5 of VK 1.1, maxImageCount of zero means "that there is no limit on
@@ -157,7 +268,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
<< utils::io::endl;
desiredImageCount = caps.minImageCount;
desiredImageCount = caps.surfaceCapabilities.minImageCount;
}
// Find a suitable surface format.
@@ -199,46 +310,50 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
<< "Desired present mode is not supported by this device.";
// Create the low-level swap chain.
if (caps.currentExtent.width == VULKAN_UNDEFINED_EXTENT
|| caps.currentExtent.height == VULKAN_UNDEFINED_EXTENT) {
auto const& currentExtent = caps.surfaceCapabilities.currentExtent;
if (currentExtent.width == VULKAN_UNDEFINED_EXTENT
|| currentExtent.height == VULKAN_UNDEFINED_EXTENT) {
mSwapChainBundle.extent = mFallbackExtent;
} else {
mSwapChainBundle.extent = caps.currentExtent;
mSwapChainBundle.extent = currentExtent;
}
VkCompositeAlphaFlagBitsKHR const compositeAlpha
= (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkCompositeAlphaFlagBitsKHR const compositeAlpha =
(caps.surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
? VK_COMPOSITE_ALPHA_INHERIT_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{
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR :
VkSwapchainCreateFlagsKHR(0),
.surface = mSurface,
.minImageCount = desiredImageCount,
.imageFormat = surfaceFormat.format,
.imageColorSpace = surfaceFormat.colorSpace,
.imageExtent = mSwapChainBundle.extent,
.imageArrayLayers = 1,
.imageUsage
= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| 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)
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : VkSwapchainCreateFlagsKHR{},
.surface = mSurface,
.minImageCount = desiredImageCount,
.imageFormat = surfaceFormat.format,
.imageColorSpace = surfaceFormat.colorSpace,
.imageExtent = mSwapChainBundle.extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transferBits,
// TODO: Setting the preTransform to IDENTITY means we are letting the Android
// Compositor handle the rotation. In some situations it might be more efficient to
// handle this ourselves by setting this field to be equal to the currentTransform mask
// in the caps, but this would involve adjusting the MVP, derivatives in GLSL, and
// possibly more.
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
// TODO: Setting the preTransform to IDENTITY means we are letting the
// Android Compositor handle the rotation. In some situations it might be
// more efficient to handle this ourselves by setting this field to be equal
// to the currentTransform mask in the caps, but this would involve
// adjusting the MVP, derivatives in GLSL, and possibly more.
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = compositeAlpha,
.presentMode = desiredPresentMode,
.clipped = VK_TRUE,
.compositeAlpha = compositeAlpha,
.presentMode = desiredPresentMode,
.clipped = VK_TRUE,
.oldSwapchain = mSwapchain,
.oldSwapchain = mSwapchain,
};
VkResult result = vkCreateSwapchainKHR(mDevice, &createInfo, VKALLOC, &mSwapchain);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
@@ -248,16 +363,16 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
mSwapChainBundle.colorFormat = surfaceFormat.format;
mSwapChainBundle.depthFormat =
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
mSwapChainBundle.isProtected = mIsProtected;
mSwapChainBundle.depth =
createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat, mIsProtected);
mSwapChainBundle.protection = mIsProtected;
FVK_LOGI << "vkCreateSwapchain"
<< ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", "
<< surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", "
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
<< "identity-transform=" << (caps.currentTransform == 1) << ", "
<< "depth=" << mSwapChainBundle.depthFormat << ", "
<< "protected=" << mSwapChainBundle.isProtected
<< "identity-transform=" << (caps.surfaceCapabilities.currentTransform == 1) << ", "
<< "depth=" << mSwapChainBundle.depthFormat
<< io::endl;
VkSemaphoreCreateInfo const semaphoreCreateInfo = {
@@ -347,6 +462,10 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
VkDevice device, VkQueue queue, VkExtent2D extent, uint64_t flags)
: VulkanPlatformSwapChainImpl(context, device, queue),
mCurrentIndex(0) {
assert_invariant(!(flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) &&
"Protected headless swapchain not supported");
mSwapChainBundle.extent = extent;
mSwapChainBundle.colorFormat = (flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0
? VK_FORMAT_R8G8B8A8_SRGB
@@ -356,13 +475,13 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
images.reserve(HEADLESS_SWAPCHAIN_SIZE);
images.resize(HEADLESS_SWAPCHAIN_SIZE);
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;
mSwapChainBundle.depthFormat =
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat);
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat, false);
}
VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() {
@@ -73,7 +73,7 @@ protected:
// Non-virtual override-able method
void destroy();
VkImage createImage(VkExtent2D extent, VkFormat format);
VkImage createImage(VkExtent2D extent, VkFormat format, bool isProtected);
VulkanContext const& mContext;
VkDevice mDevice;
+7 -6
View File
@@ -280,13 +280,14 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
SYSTRACE_CALL();
#if 0 && defined(__ANDROID__)
char scratch[PROP_VALUE_MAX + 1];
int length = __system_property_get("debug.filament.protected", scratch);
if (swapChain && length > 0) {
#if 1 && defined(__ANDROID__)
// char scratch[PROP_VALUE_MAX + 1];
// int length = __system_property_get("debug.filament.protected", scratch);
// if (swapChain && length > 0) {
if (swapChain) {
uint64_t flags = swapChain->getFlags();
bool value = bool(atoi(scratch));
if (value) {
// bool value = bool(atoi(scratch));
if (true) {
flags |= SwapChain::CONFIG_PROTECTED_CONTENT;
} else {
flags &= ~SwapChain::CONFIG_PROTECTED_CONTENT;
+2 -5
View File
@@ -35,11 +35,8 @@ DescriptorSetLayout::DescriptorSetLayout(
backend::DescriptorSetLayout descriptorSetLayout) noexcept {
for (auto&& desc : descriptorSetLayout.bindings) {
mMaxDescriptorBinding = std::max(mMaxDescriptorBinding, desc.binding);
mSamplers.set(desc.binding,
desc.type == backend::DescriptorType::SAMPLER ||
desc.type == backend::DescriptorType::SAMPLER_EXTERNAL);
mUniformBuffers.set(desc.binding,
desc.type == backend::DescriptorType::UNIFORM_BUFFER);
mSamplers.set(desc.binding, desc.type == backend::DescriptorType::SAMPLER);
mUniformBuffers.set(desc.binding, desc.type == backend::DescriptorType::UNIFORM_BUFFER);
}
mDescriptorSetLayoutHandle = factory.create(driver,
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.56.0"
spec.version = "1.55.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.0/filament-v1.56.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.55.0/filament-v1.55.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 56;
static constexpr size_t MATERIAL_VERSION = 55;
/**
* Supported shading models
+4 -11
View File
@@ -72,8 +72,6 @@ const char* toString(DescriptorType type) {
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
@@ -137,9 +135,7 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
for (auto const& sampler : samplers) {
layout.bindings.push_back(DescriptorSetLayoutBinding {
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) ?
DescriptorType::SAMPLER_EXTERNAL : DescriptorType::SAMPLER,
layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::SAMPLER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, sampler.binding,
DescriptorFlags::NONE, 0 });
}
@@ -215,8 +211,7 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
for (size_t i = 0; i < info.bindings.size(); i++) {
backend::descriptor_binding_t binding = info.bindings[i].binding;
auto name = getDescriptorName(set, binding);
if (info.bindings[i].type == DescriptorType::SAMPLER ||
info.bindings[i].type == DescriptorType::SAMPLER_EXTERNAL) {
if (info.bindings[i].type == DescriptorType::SAMPLER) {
auto pos = std::find_if(samplerList.begin(), samplerList.end(),
[&](const auto& entry) { return entry.binding == binding; });
assert_invariant(pos != samplerList.end());
@@ -250,8 +245,7 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) {
printf("[DS] info (%s) = [\n", getName(setIndex));
for (auto const& descriptor : descriptors) {
auto const& [name, info, sampler] = descriptor;
if (info.type == DescriptorType::SAMPLER ||
info.type == DescriptorType::SAMPLER_EXTERNAL) {
if (info.type == DescriptorType::SAMPLER) {
assert_invariant(sampler.has_value());
printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = "
"%s, samplerType = %s}",
@@ -462,8 +456,7 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
assert_invariant(sampler.has_value());
const std::string samplerName = std::string(name.c_str()) + "Smplr";
argBufferBuilder
@@ -202,11 +202,7 @@ void MaterialDescriptorBindingsChuck::flatten(Flattener& f) {
// all the material's sampler descriptors
for (auto const& entry: mSamplerInterfaceBlock.getSamplerInfoList()) {
f.writeString({ entry.uniformName.data(), entry.uniformName.size() });
if (entry.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) {
f.writeUint8(uint8_t(DescriptorType::SAMPLER_EXTERNAL));
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
f.writeUint8(entry.binding);
}
@@ -260,11 +256,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
// all the material's sampler descriptors
for (auto const& entry: mSamplerInterfaceBlock.getSamplerInfoList()) {
if (entry.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) {
f.writeUint8(uint8_t(DescriptorType::SAMPLER_EXTERNAL));
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(entry.binding);
f.writeUint8(uint8_t(DescriptorFlags::NONE));
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.56.0",
"version": "1.55.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",