-
Notifications
You must be signed in to change notification settings - Fork 2k
Stream API Vulkan Backend #9164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 88 commits
6218164
e265850
8671617
d49554d
4fccd95
96cba96
abe8a49
c81cedd
387942c
0fdf1ca
8bc4d3a
9e92193
01a2480
48bb10a
ff145f6
c5c2d42
3b0e44c
80803f9
ad3a5b7
be6b6ae
021c4f6
0a661c1
ea4c20e
ba99844
cfe6a42
7f7dadd
b570d48
b09cbaf
13e1d2a
ff2827b
55fc91b
bcae04c
2fac13e
d45a265
c00afac
84fc69e
1b4f88b
839867e
397282a
75fdacb
111ac62
19cbb94
c612bfd
3c93f00
487fb27
fbf2f0d
8b6fa40
f548c58
bee08b4
76fc21a
5858acf
e441cbb
5a0f78d
865cb0a
a220f70
c6d0127
8017d5e
ad57838
0210611
1e5d37a
29e3f07
406a348
3a1aebe
0c07c3f
f503205
6e8959e
39be20b
db83dc0
54c636d
562eeed
74ffe4e
32ca1d3
3457700
85c899a
0ba9c2a
78d5e34
1216024
e022396
48cd99a
83a89b5
1a390f4
c9b3104
0a11d0c
86f0afe
44b506b
df284fe
f96afef
e4f29b0
56831e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ | |
#include "vulkan/memory/ResourcePointer.h" | ||
#include "vulkan/utils/Conversion.h" | ||
#include "vulkan/utils/Definitions.h" | ||
#include "vulkan/utils/Image.h" | ||
|
||
#include <backend/DriverEnums.h> | ||
#include <backend/platforms/VulkanPlatform.h> | ||
|
@@ -145,6 +146,31 @@ static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) { | |
cbData->cb(cbData->sync, cbData->userData); | ||
}; | ||
|
||
// This needs to be discussed because Vulkan has different default Matrix formats | ||
// right now this is following GL. | ||
static void copyMat3f(void* addr, size_t const offset, const math::mat3f& v) noexcept { | ||
struct mat43 { | ||
float v[3][4]; | ||
}; | ||
|
||
addr = static_cast<char*>(addr) + offset; | ||
mat43& temp = *static_cast<mat43*>(addr); | ||
|
||
temp.v[0][0] = v[0][0]; | ||
temp.v[0][1] = v[0][1]; | ||
temp.v[0][2] = v[0][2]; | ||
|
||
temp.v[1][0] = v[1][0]; | ||
temp.v[1][1] = v[1][1]; | ||
temp.v[1][2] = v[1][2]; | ||
|
||
temp.v[2][0] = v[2][0]; | ||
temp.v[2][1] = v[2][1]; | ||
temp.v[2][2] = v[2][2]; | ||
|
||
// don't store anything in temp.v[][3] because there could be uniforms packed there | ||
} | ||
|
||
}// anonymous namespace | ||
|
||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) | ||
|
@@ -239,6 +265,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context, | |
mQueryManager(mPlatform->getDevice()), | ||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache, | ||
&mDescriptorSetLayoutCache), | ||
mStreamedImageManager(&mExternalImageManager, &mDescriptorSetCache, &mSamplerCache), | ||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported), | ||
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain | ||
mStereoscopicType(driverConfig.stereoscopicType) { | ||
|
@@ -466,13 +493,17 @@ void VulkanDriver::updateDescriptorSetTexture( | |
if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) { | ||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params); | ||
mAppState.hasBoundExternalImages = true; | ||
} else if (bool(texture->getStream())) { | ||
mStreamedImageManager.bindStreamedTexture(set, binding, texture, params); | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
mAppState.hasBoundExternalImages = true; | ||
} else { | ||
VulkanSamplerCache::Params cacheParams = { | ||
.sampler = params, | ||
}; | ||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams); | ||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler); | ||
mExternalImageManager.clearTextureBinding(set, binding); | ||
mStreamedImageManager.unbindStreamedTexture(set, binding); | ||
} | ||
} | ||
|
||
|
@@ -1050,6 +1081,12 @@ void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) { | |
} | ||
|
||
void VulkanDriver::destroyStream(Handle<HwStream> sh) { | ||
FVK_SYSTRACE_SCOPE(); | ||
if (!sh) { | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
} | ||
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh); | ||
stream.dec(); | ||
} | ||
|
||
void VulkanDriver::destroySync(Handle<HwSync> sh) { | ||
|
@@ -1087,25 +1124,112 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) { | |
} | ||
|
||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::CString tag) { | ||
FILAMENT_CHECK_PRECONDITION(false) << "createStreamNative not supported in Vulkan."; | ||
return {}; | ||
} | ||
|
||
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::CString tag) { | ||
return {}; | ||
// @TODO This is still not thread-safe. We might have to revisit this question. | ||
FVK_SYSTRACE_SCOPE(); | ||
auto handle = mResourceManager.allocHandle<VulkanStream>(); | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
auto stream = resource_ptr<VulkanStream>::make(&mResourceManager, handle); | ||
stream.inc(); | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return handle; | ||
} | ||
|
||
void VulkanDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform, | ||
CallbackHandler* handler, StreamCallback cb, void* userData) { | ||
FVK_SYSTRACE_SCOPE(); | ||
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh); | ||
assert_invariant(stream->streamType == StreamType::ACQUIRED); | ||
assert_invariant(image != nullptr); | ||
// This covers the case where setAcquiredImage is called back to back (no updateStreams in between) | ||
if (stream->previousNeedsRelease()) { | ||
scheduleRelease(stream->takePrevious()); | ||
} | ||
stream->acquire({ image, cb, userData, handler}); | ||
stream->setFrontEndTransform(transform); | ||
mStreamsWithPendingAcquiredImage.push_back(stream); | ||
poweifeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
void VulkanDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) { | ||
FVK_SYSTRACE_SCOPE(); | ||
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh); | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
stream->width = width; | ||
stream->height = height; | ||
} | ||
|
||
int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) { | ||
return 0; | ||
} | ||
|
||
void VulkanDriver::updateStreams(CommandStream* driver) { | ||
FVK_SYSTRACE_SCOPE(); | ||
if (UTILS_UNLIKELY(!mStreamsWithPendingAcquiredImage.empty())) { | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (auto& stream: mStreamsWithPendingAcquiredImage) { | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (stream->previousNeedsRelease()) { | ||
scheduleRelease(stream->takePrevious()); | ||
} | ||
|
||
// This executes on the backend thread (updateStreams is synchonous which means it | ||
// executes on the user thread) Note: stream is captured by copy which is fine, this is | ||
// a copy of a resource_ptr<VulkanStream>. We only need it find the associated stream | ||
// inside the mStreamedImageManager texture bindings | ||
driver->queueCommand([this, stream, s = stream.get(), | ||
poweifeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
image = stream->getAcquired().image, | ||
transform = stream->getFrontEndTransform()]() { | ||
auto texture = s->getTexture(image); | ||
s->setBackendTransform(transform); | ||
if (!texture) { | ||
auto externalImage = fvkutils::createExternalImageFromRaw(mPlatform, image, false); | ||
auto metadata = mPlatform->extractExternalImageMetadata(externalImage); | ||
auto imgData = mPlatform->createVkImageFromExternal(externalImage); | ||
|
||
assert_invariant(imgData.internal.valid() || imgData.external.valid()); | ||
|
||
VkFormat vkformat = metadata.format; | ||
VkImage vkimage = VK_NULL_HANDLE; | ||
VkDeviceMemory memory = VK_NULL_HANDLE; | ||
if (imgData.internal.valid()) { | ||
metadata.externalFormat = 0; | ||
vkimage = imgData.internal.image; | ||
memory = imgData.internal.memory; | ||
} else { | ||
vkformat = VK_FORMAT_UNDEFINED; | ||
vkimage = imgData.external.image; | ||
memory = imgData.external.memory; | ||
} | ||
|
||
VkSamplerYcbcrConversion const conversion = | ||
mExternalImageManager.getVkSamplerYcbcrConversion(metadata); | ||
|
||
auto newTexture = resource_ptr<VulkanTexture>::construct(&mResourceManager, | ||
mContext, mPlatform->getDevice(), mAllocator, &mResourceManager, | ||
&mCommands, vkimage, memory, vkformat, conversion, metadata.samples, | ||
metadata.width, metadata.height, metadata.layers, | ||
metadata.filamentUsage, mStagePool); | ||
|
||
auto& commands = mCommands.get(); | ||
// Unlike uploaded textures or swapchains, we need to explicit transition this | ||
// texture into the read layout. | ||
newTexture->transitionLayout(&commands, newTexture->getPrimaryViewRange(), | ||
VulkanLayout::FRAG_READ); | ||
|
||
if (imgData.external.valid()) { | ||
mExternalImageManager.addExternallySampledTexture(newTexture, | ||
externalImage); | ||
// Cache the AHB backed image. Acquires the image here. | ||
s->pushImage(image, newTexture); | ||
} | ||
|
||
texture = newTexture; | ||
} | ||
|
||
mStreamedImageManager.onStreamAcquireImage(texture, stream); | ||
}); | ||
} | ||
mStreamsWithPendingAcquiredImage.clear(); | ||
} | ||
} | ||
|
||
void VulkanDriver::destroyFence(Handle<HwFence> fh) { | ||
|
@@ -1378,7 +1502,9 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor | |
|
||
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, | ||
BufferObjectStreamDescriptor&& streams) { | ||
// Noop | ||
|
||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh); | ||
mStreamUniformDescriptors[bo.get()] = std::move(streams); | ||
} | ||
|
||
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd, | ||
|
@@ -1389,6 +1515,33 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript | |
commands.acquire(bo); | ||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size); | ||
|
||
if (UTILS_UNLIKELY(!mStreamUniformDescriptors.empty())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldnt the copyMat3f happen before bo->loadFromCpu? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that's right, good catch. |
||
auto streamDescriptors = mStreamUniformDescriptors.find(bo.get()); | ||
if (streamDescriptors != mStreamUniformDescriptors.end()) { | ||
for (auto const& [offset, streamHandle, associationType]: | ||
streamDescriptors->second.mStreams) { | ||
if (associationType == BufferObjectStreamAssociationType::TRANSFORM_MATRIX) { | ||
math::mat3f transform; | ||
if (streamHandle) { | ||
auto stream = | ||
resource_ptr<VulkanStream>::cast(&mResourceManager, streamHandle); | ||
if (stream->streamType == StreamType::NATIVE) { | ||
FILAMENT_CHECK_PRECONDITION(false) | ||
<< "Native Stream not supported in Vulkan."; | ||
} else { | ||
// Backend call since getStreamTransformMatrix is called from async | ||
// updateBufferObject. | ||
transform = stream->getBackEndTransform(); | ||
} | ||
} | ||
|
||
copyMat3f(bd.buffer, offset, transform); | ||
} | ||
} | ||
mStreamUniformDescriptors.erase(streamDescriptors); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we have to erase this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the logic is that the user has to submit an update to the stream descriptor every time the xform changes. Once we process it, we can remove it from the list. |
||
} | ||
} | ||
|
||
scheduleDestroy(std::move(bd)); | ||
} | ||
|
||
|
@@ -1454,6 +1607,12 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint | |
} | ||
|
||
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) { | ||
FVK_SYSTRACE_SCOPE(); | ||
auto t = resource_ptr<VulkanTexture>::cast(&mResourceManager, th); | ||
phoenixxxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert_invariant(t); | ||
auto s = resource_ptr<VulkanStream>::cast(&mResourceManager, sh); | ||
assert_invariant(s); | ||
t->setStream(s); | ||
} | ||
|
||
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can a texture be external sampled and streamed at the same time? or are they mutually exclusive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logically yes. But that would mean that a texture that is external was attached to a stream. And that seems like something went wrong, because from the frontend's perspective you can create a texture one of 3 ways:
Although you could logically do 2) and then set a stream via 3, I question the meaning of that.