Skip to content

Commit

Permalink
Texture sharing between OpenGL and Vulkan
Browse files Browse the repository at this point in the history
  • Loading branch information
ksuprynowicz committed Dec 30, 2024
1 parent c1e266e commit 7b103e0
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ bool VulkanDisplayPlugin::activate() {
}
//CHECK_GL_ERROR();
widget->context()->doneCurrent();
widget->context()->moveToThread(presentThread.get());

presentThread->setContext(&vks::Context::get());
connect(presentThread.data(), &QThread::started, [] { setThreadName("OpenGL Present Thread"); });
Expand Down Expand Up @@ -752,9 +753,12 @@ void VulkanDisplayPlugin::present(const std::shared_ptr<RefreshRateController>&
});
// Execute the frame rendering commands
PROFILE_RANGE_EX(render, "execute", 0xff00ff00, frameId)

auto context = _container->getPrimaryWidget()->context();
context->moveToThread(QThread::currentThread());
context->makeCurrent();
vkBackend->setDrawCommandBuffer(commandBuffer);
_gpuContext->executeFrame(_currentFrame);
context->doneCurrent();
_renderedFrameCount++;
qDebug() << "Frame rendered: " << _renderedFrameCount;
}
Expand Down
36 changes: 29 additions & 7 deletions libraries/gpu-vk/src/gpu/vk/VKBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ BackendPointer VKBackend::createBackend() {
// FIXME provide a mechanism to override the backend for testing
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
std::shared_ptr<VKBackend> result = std::make_shared<VKBackend>();
result->initTransform();
result->initDefaultTexture();
INSTANCE = result.get();
void* voidInstance = &(*result);
qApp->setProperty(VK_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance));
Expand Down Expand Up @@ -203,6 +201,13 @@ Cache _cache;

void VKBackend::executeFrame(const FramePointer& frame) {
using namespace vks::debugutils;

// Initialize parts that cannot be initialized in default constructor.
if (!_isInitialized) {
initBeforeFirstFrame();
_isInitialized = true;
}

// Create descriptor pool
// VKTODO: delete descriptor pool after it's not needed
//_frameData._descriptorPool
Expand Down Expand Up @@ -1385,9 +1390,12 @@ VKTexture* VKBackend::syncGPUObject(const Texture& texture) {
/*if (!texture) {
return nullptr;
}*/
VKTexture* object = Backend::getGPUObject<VKTexture>(texture);

if (TextureUsageType::EXTERNAL == texture.getUsageType()) {
/*Texture::ExternalUpdates updates = texture.getUpdates();
Q_ASSERT(GLAD_GL_EXT_memory_object);
Q_ASSERT(GLAD_GL_EXT_semaphore);
Texture::ExternalUpdates updates = texture.getUpdates();
if (!updates.empty()) {
Texture::ExternalRecycler recycler = texture.getExternalRecycler();
Q_ASSERT(recycler);
Expand All @@ -1400,7 +1408,7 @@ VKTexture* VKBackend::syncGPUObject(const Texture& texture) {
// work is involved
recycler(update.first, update.second);
updates.pop_front();
}
} // VKTODO: should that be thread-safe?

// The last texture remaining is the one we'll use to create the GLTexture
const auto& update = updates.front();
Expand All @@ -1411,13 +1419,23 @@ VKTexture* VKBackend::syncGPUObject(const Texture& texture) {
glDeleteSync(fence);
}

if (!object) {
object = new VKExternalTexture(shared_from_this(), texture);
}
auto externalTexture = dynamic_cast<VKExternalTexture*>(object);
Q_ASSERT(externalTexture);
externalTexture->setSource(update.first);
externalTexture->transferGL(*this); // VKTODO: add texture resizing if needed

// Create the new texture object (replaces any previous texture object)
new GLExternalTexture(shared_from_this(), texture, update.first);

return object;
//new GLExternalTexture(shared_from_this(), texture, update.first);
}

// Return the texture object (if any) associated with the texture, without extensive logic
// (external textures are
return Backend::getGPUObject<GLTexture>(texture);*/
//return Backend::getGPUObject<GLTexture>(texture);*/

//return Parent::syncGPUObject(texturePointer);
}
Expand All @@ -1427,7 +1445,6 @@ VKTexture* VKBackend::syncGPUObject(const Texture& texture) {
return nullptr;
}

VKTexture* object = Backend::getGPUObject<VKTexture>(texture);
// VKTODO: check object->_storageStamp to see if texture is outdated
if (!object) {
switch (texture.getUsageType()) {
Expand Down Expand Up @@ -1984,6 +2001,11 @@ void VKBackend::beforeShutdownCleanup() {
perFrameCleanup();
}

void VKBackend::initBeforeFirstFrame() {
initTransform();
initDefaultTexture();
}

void VKBackend::initTransform() {

#ifdef GPU_SSBO_TRANSFORM_OBJECT
Expand Down
4 changes: 4 additions & 0 deletions libraries/gpu-vk/src/gpu/vk/VKBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ class VKBackend : public Backend, public std::enable_shared_from_this<VKBackend>
virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final;

protected:
// Initializes parts of the backend that can't be initialized in the constuctor.
void initBeforeFirstFrame();

void initTransform();
void initDefaultTexture();

Expand Down Expand Up @@ -487,6 +490,7 @@ class VKBackend : public Backend, public std::enable_shared_from_this<VKBackend>
static std::array<VKBackend::CommandCall, Batch::NUM_COMMANDS> _commandCalls;
static const size_t INVALID_OFFSET = (size_t)-1;
static size_t UNIFORM_BUFFER_OFFSET_ALIGNMENT;
bool _isInitialized {false};
};

}} // namespace gpu::vulkan
Expand Down
75 changes: 70 additions & 5 deletions libraries/gpu-vk/src/gpu/vk/VKTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <QtCore/QThread>
#include <NumericalConstants.h>
#include <gl/GLHelpers.h>

#include "VKBackend.h"
#include "vk/Allocation.h"
Expand Down Expand Up @@ -609,6 +610,7 @@ void VKExternalTexture::createTexture(VKBackend &backend) {
imageCI.extent.height = _gpuObject.getHeight();
imageCI.extent.depth = 1;
imageCI.arrayLayers = _gpuObject.isArray() ? _gpuObject.getNumSlices() : 1;
imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCI.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Expand All @@ -617,7 +619,7 @@ void VKExternalTexture::createTexture(VKBackend &backend) {
imageCI.arrayLayers = 6;
}

_transferData.mipLevels = _gpuObject.getNumMips();
_transferData.mipLevels = 1; // VKTODO: generate mipmaps for web textures later
_transferData.width = _gpuObject.getWidth();
_transferData.height = _gpuObject.getHeight();

Expand Down Expand Up @@ -689,7 +691,17 @@ void VKExternalTexture::createTexture(VKBackend &backend) {
}
}*/

imageCI.mipLevels = _transferData.mips.size();
imageCI.mipLevels = 1;

VkExternalMemoryImageCreateInfo externalMemoryImageCI {};
externalMemoryImageCI.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
#ifdef WIN32
externalMemoryImageCI.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
#else
externalMemoryImageCI.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
#endif
imageCI.pNext = &externalMemoryImageCI;

VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCI, nullptr, &_vkImage));

VkMemoryRequirements memoryRequirements;
Expand All @@ -714,7 +726,6 @@ void VKExternalTexture::createTexture(VKBackend &backend) {
memoryAllocateInfo.pNext = &exportMemoryAllocateInfo;

VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memoryAllocateInfo, nullptr, &_sharedMemory));
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, _vkImage, _sharedMemory, 0));
#ifdef WIN32
VkMemoryGetWin32HandleInfoKHR memoryGetWin32HandleInfoKHR {};
memoryGetWin32HandleInfoKHR.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
Expand All @@ -728,20 +739,74 @@ void VKExternalTexture::createTexture(VKBackend &backend) {
memoryGetFdInfoKHR.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
VK_CHECK_RESULT(vkGetMemoryFdKHR(device->logicalDevice, &memoryGetFdInfoKHR, &_sharedFd));
#endif
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, _vkImage, _sharedMemory, 0));

VkCommandBuffer transferCmd = device->createCommandBuffer(device->graphicsCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);

VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1; // VKTODO
if (_gpuObject.getType() == Texture::TEX_CUBE) {
subresourceRange.layerCount = 6;
}else{
subresourceRange.layerCount = 1;
}

vks::tools::setImageLayout(
transferCmd,
_vkImage,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresourceRange);

VkClearColorValue color = { .float32 = {0.5, 0.0, 0.0} };
VkImageSubresourceRange imageSubresourceRange { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdClearColorImage(transferCmd, _vkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &imageSubresourceRange);

_vkImageLayout = VK_IMAGE_LAYOUT_GENERAL;

vks::tools::setImageLayout(
transferCmd,
_vkImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL,
subresourceRange);

device->flushCommandBuffer(transferCmd, backend.getContext().graphicsQueue, device->graphicsCommandPool);
}

void VKExternalTexture::initGL(gpu::vk::VKBackend& backend) {
glCreateMemoryObjectsEXT(1, &_openGLMemoryObject);
glCreateMemoryObjectsEXT(1, &_openGLMemoryObject); // VKTODO: clean these up later
::gl::checkGLError("GL to VK");
#ifdef WIN32
glImportMemoryWin32HandleEXT(_openGLMemoryObject, _sharedMemorySize, GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, _sharedHandle);
::gl::checkGLError("GL to VK");
#else
glImportMemoryFdEXT(_openGLMemoryObject, _sharedMemorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, _sharedFd);
_sharedFd = -1; // File descriptor can be used only once?
::gl::checkGLError("GL to VK");
#endif

glCreateTextures(GL_TEXTURE_2D, 1, &_openGLId);
::gl::checkGLError("GL to VK");
Q_ASSERT(evalTexelFormatInternal(_gpuObject.getTexelFormat()) == VK_FORMAT_R8G8B8A8_UNORM);
glTextureStorageMem2DEXT(_openGLId, 1, GL_RGBA8, _gpuObject.getWidth(), _gpuObject.getHeight(), _openGLMemoryObject, 0);
::gl::checkGLError("GL to VK");
std::array<GLubyte, 4> clearColor {128,128,128,255};
glClearTexImage(_openGLId, 0, GL_RGBA, GL_UNSIGNED_BYTE, clearColor.data());
::gl::checkGLError("GL to VK");
}

void VKExternalTexture::transferGL(VKBackend &backend) {

glCopyImageSubData(
_openGLSourceId, GL_TEXTURE_2D
, 0, 0, 0, 0
, _openGLId, GL_TEXTURE_2D
, 0, 0, 0, 0
, _gpuObject.getWidth(), _gpuObject.getHeight(), 1);
::gl::checkGLError("GL to VK");
// VKTODO: generate mipmaps
}

void VKExternalTexture::postTransfer(VKBackend &backend) {
Expand Down
8 changes: 5 additions & 3 deletions libraries/gpu-vk/src/gpu/vk/VKTexture.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,12 @@ class VKExternalTexture: public VKTexture {
VKExternalTexture::createTexture(vkBackend);
// VKTODO: maybe this needs to be done on OpenGL thread?
VKExternalTexture::initGL(vkBackend);
VKExternalTexture::transferGL(vkBackend);
//VKExternalTexture::transferGL(vkBackend);
VKExternalTexture::postTransfer(vkBackend);
}; // VKTODO
~VKExternalTexture() override;
~VKExternalTexture() override; // VKTODO: add proper cleanup for both Vulkan and OpenGL
void setSource(GLuint source) { _openGLSourceId = source; };
void transferGL(VKBackend &backend);

protected:
Size size() const override { return _size; }
Expand All @@ -337,7 +339,6 @@ class VKExternalTexture: public VKTexture {

void createTexture(VKBackend &backend) override;
void initGL(VKBackend &backend);
void transferGL(VKBackend &backend);
void transfer(VKBackend &backend) override {};
void postTransfer(VKBackend &backend) override;
VkDescriptorImageInfo getDescriptorImageInfo() override;
Expand All @@ -353,6 +354,7 @@ class VKExternalTexture: public VKTexture {
#endif
GLuint _openGLMemoryObject = 0;
GLuint _openGLId = 0;
GLuint _openGLSourceId = 0;
};

} }
Expand Down

0 comments on commit 7b103e0

Please sign in to comment.