Cleanup and fix for reflections

This commit is contained in:
Karol Suprynowicz 2024-12-13 23:32:28 +01:00
parent 0c8b3d4b54
commit e18d4818cf
14 changed files with 231 additions and 865 deletions

View file

@ -351,8 +351,8 @@ void VulkanDisplayPlugin::deactivate() {
_container->currentDisplayActions().clear();
}
// VKTODO: Should this be here?
uncustomizeContext();
// VKTODO: This shouldn't be here, because it's wrong thread, but then where?
//uncustomizeContext();
Parent::deactivate();
}

View file

@ -64,6 +64,8 @@ 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));
@ -111,8 +113,6 @@ VKBackend::VKBackend() {
_framePool.push_back(std::make_shared<FrameData>(this));
_framesToReuse.push_back(_framePool.back());
}
initTransform();
initDefaultTexture();
}
VKBackend::~VKBackend() {
@ -832,9 +832,8 @@ void VKBackend::setDrawCommandBuffer(VkCommandBuffer commandBuffer) {
_currentCommandBuffer = commandBuffer;
}
void VKBackend::trash(VKBuffer& buffer) {
// VKTODO: thread safety for this and similar calls
buffer.destroy();
VkDescriptorImageInfo VKBackend::getDefaultTextureDescriptorInfo() {
return _defaultTextureVk->getDescriptorImageInfo();
}
void VKBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo, Vec2u framebufferSize) {
@ -1224,7 +1223,7 @@ void VKBackend::updateVkDescriptorWriteSetsTexture(VkDescriptorSet target) {
qDebug() << "Texture is null during descriptor " << i
<< " write: " << _resource._textures[i].texture->source();
}
imageInfo = _defaultTexture.descriptor;
imageInfo = _defaultTextureImageInfo;
}
//imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
//imageInfo.imageView = texture->;
@ -1251,7 +1250,7 @@ void VKBackend::updateVkDescriptorWriteSetsTexture(VkDescriptorSet target) {
descriptorWriteSet.dstArrayElement = 0;
descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWriteSet.descriptorCount = 1;
descriptorWriteSet.pImageInfo = &_defaultTexture.descriptor;
descriptorWriteSet.pImageInfo = &_defaultTextureImageInfo;
sets.push_back(descriptorWriteSet);
}
}
@ -1993,7 +1992,13 @@ void VKBackend::initDefaultTexture() {
buffer[x + y * width + 3] = 255;
}
}
_defaultTexture.fromBuffer(buffer.data(), buffer.size(), VK_FORMAT_R8G8B8A8_SRGB, width, height, _context.device.get(), _context.transferQueue);
_defaultTexture = gpu::Texture::create2D(gpu::Element{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }, width, height, 1U,
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP));
_defaultTexture->setStoredMipFormat(_defaultTexture->getTexelFormat());
_defaultTexture->assignStoredMip(0, width * height * sizeof(uint8_t) * 4, (const gpu::Byte*)buffer.data());
_defaultTextureVk = syncGPUObject(*_defaultTexture);
_defaultTextureImageInfo = _defaultTextureVk->getDescriptorImageInfo();
}
void VKBackend::acquireFrameData() {
@ -2016,6 +2021,149 @@ void VKBackend::waitForGPU() {
VK_CHECK_RESULT(vkDeviceWaitIdle(_context.device->logicalDevice));
}
void VKBackend::Recycler::trashVkSampler(VkSampler& sampler) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkSamplers.push_back(sampler);
}
void VKBackend::Recycler::trashVkFramebuffer(VkFramebuffer& framebuffer) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkFramebuffer.push_back(framebuffer);
}
void VKBackend::Recycler::trashVkImageView(VkImageView& imageView) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkImageViews.push_back(imageView);
}
void VKBackend::Recycler::trashVkImage(VkImage& image) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkImages.push_back(image);
}
void VKBackend::Recycler::trashVkBuffer(VkBuffer& buffer) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkBuffers.push_back(buffer);
}
void VKBackend::Recycler::trashVkRenderPass(VkRenderPass& renderPass) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkRenderPasses.push_back(renderPass);
}
void VKBackend::Recycler::trashVkPipeline(VkPipeline& pipeline) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkPipelines.push_back(pipeline);
}
void VKBackend::Recycler::trashVkShaderModule(VkShaderModule& module) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
vkShaderModules.push_back(module);
}
void VKBackend::Recycler::trashVmaAllocation(VmaAllocation& allocation) {
std::lock_guard<std::recursive_mutex> lockGuard(recyclerMutex);
}
void VKBackend::perFrameCleanup() {
std::lock_guard<std::recursive_mutex> lockGuard(recycler.recyclerMutex);
// Remove pointers to objects that were deleted during the frame.
for (auto framebuffer : recycler.deletedFramebuffers) {
framebuffers.erase(framebuffer);
}
size_t capacityBeforeClear = recycler.deletedFramebuffers.capacity();
recycler.deletedFramebuffers.resize(0);
recycler.deletedFramebuffers.reserve(capacityBeforeClear);
for (auto buffer : recycler.deletedBuffers) {
buffers.erase(buffer);
}
capacityBeforeClear = recycler.deletedBuffers.capacity();
recycler.deletedBuffers.resize(0);
recycler.deletedBuffers.reserve(capacityBeforeClear);
for (auto texture : recycler.deletedTextures) {
textures.erase(texture);
}
capacityBeforeClear = recycler.deletedTextures.capacity();
recycler.deletedTextures.resize(0);
recycler.deletedTextures.reserve(capacityBeforeClear);
for (auto query : recycler.deletedQueries) {
queries.erase(query);
}
capacityBeforeClear = recycler.deletedQueries.capacity();
recycler.deletedQueries.resize(0);
recycler.deletedQueries.reserve(capacityBeforeClear);
auto device = _context.device->logicalDevice;
for (auto sampler : recycler.vkSamplers) {
vkDestroySampler(device, sampler, nullptr);
}
for (auto imageView : recycler.vkImageViews) {
vkDestroyImageView(device, imageView, nullptr);
}
for (auto image : recycler.vkImages) {
vkDestroyImage(device, image, nullptr);
}
for (auto buffer : recycler.vkBuffers) {
vkDestroyBuffer(device, buffer, nullptr);
}
for (auto renderPass: recycler.vkRenderPasses) {
vkDestroyRenderPass(device, renderPass, nullptr);
}
for (auto pipeline: recycler.vkPipelines) {
vkDestroyPipeline(device, pipeline, nullptr);
}
for (auto allocation : recycler.vmaAllocations) {
vmaFreeMemory(vks::Allocation::getAllocator(), allocation);
}
}
void VKBackend::beforeShutdownCleanup() {
// Lock prevents destroying objects while hashes
std::lock_guard<std::recursive_mutex> lockGuard(recycler.recyclerMutex);
// Remove pointers to objects that were deleted during the frame.
// This prevents access-after-delete.
perFrameCleanup();
// Delete remaining backend objects.
for (auto framebuffer : framebuffers) {
framebuffer->_gpuObject.gpuObject.setGPUObject(nullptr);
}
framebuffers.clear();
for (auto buffer : buffers) {
buffer->_gpuObject.gpuObject.setGPUObject(nullptr);
}
buffers.clear();
for (auto texture : textures) {
texture->_gpuObject.gpuObject.setGPUObject(nullptr);
}
textures.clear();
for (auto query : queries) {
query->_gpuObject.gpuObject.setGPUObject(nullptr);
}
queries.clear();
// Deleted objects got added to recycler, so they need to be cleaned since sets are already empty.
recycler.deletedFramebuffers.clear();
recycler.deletedBuffers.clear();
recycler.deletedTextures.clear();
recycler.deletedQueries.clear();
// One more cleanup to destroy Vulkan objects released by backend objects.
perFrameCleanup();
}
void VKBackend::initTransform() {

View file

@ -263,7 +263,6 @@ protected:
void updateRenderPass();
void resetRenderPass();
// VKTODO: one instance per each frame
// Contains objects that are created per frame and need to be deleted after the frame is rendered
class FrameData {
public:
@ -289,6 +288,46 @@ protected:
VKBackend *_backend;
};
// Contains objects that need to be deleted on Vulkan backend thread after frame is rendered.
// It's filled by destructors of objects like gpu::Texture and gpu::Buffer, since these destroy
// backend counterpart of their objects.
public:
class Recycler {
public:
// This means that for every GPU object mutex will be locked and unlocked several times.
// VKTODO: It would be good to do profiling and check if it impacts performance or not.
void trashVkSampler(VkSampler &sampler);
void trashVkFramebuffer(VkFramebuffer &framebuffer);
void trashVkImageView(VkImageView &imageView);
void trashVkImage(VkImage &image);
void trashVkBuffer(VkBuffer &buffer);
void trashVkRenderPass(VkRenderPass &renderPass);
void trashVkPipeline(VkPipeline &pipeline);
void trashVkShaderModule(VkShaderModule &module);
void trashVmaAllocation(VmaAllocation &allocation);
private:
std::recursive_mutex recyclerMutex;
std::vector<VkSampler> vkSamplers;
std::vector<VkFramebuffer> vkFramebuffer;
std::vector<VkImageView> vkImageViews;
std::vector<VkImage> vkImages;
std::vector<VkBuffer> vkBuffers;
std::vector<VkRenderPass> vkRenderPasses;
std::vector<VkPipeline> vkPipelines;
std::vector<VkShaderModule> vkShaderModules;
std::vector<VmaAllocation> vmaAllocations;
// List of pointers to objects that were deleted and need to be removed from backend object sets.
std::vector<VKFramebuffer*> deletedFramebuffers;
std::vector<VKBuffer*> deletedBuffers;
std::vector<VKTexture*> deletedTextures;
std::vector<VKQuery*> deletedQueries;
friend class VKBackend;
} recycler;
private:
void draw(VkPrimitiveTopology mode, uint32 numVertices, uint32 startVertex);
void renderPassTransfer(const Batch& batch);
void renderPassDraw(const Batch& batch);
@ -320,13 +359,10 @@ public:
void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) final;
void setDrawCommandBuffer(VkCommandBuffer commandBuffer);
size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); }
VkDescriptorImageInfo getDefaultTextureDescriptorInfo() { return _defaultTexture.descriptor; };
VkDescriptorImageInfo getDefaultTextureDescriptorInfo() ;
// Used by GPU frame player to move camera around
void enableContextViewCorrectionForFramePlayer() { _transform._viewCorrectionEnabledForFramePlayer = true; };
void trash(VKBuffer& buffer);
static gpu::Primitive getPrimitiveTopologyFromCommand(Batch::Command command, const Batch& batch, size_t paramOffset);
// Draw Stage
@ -422,6 +458,17 @@ public:
VKFramebuffer *_outputTexture{ nullptr };
protected:
// These are filled by syncGPUObject() calls, and are needed to track backend objects so that they can be destroyed before
// destroying backend.
// Access to these objects happens only from the backend thread. Destructors don't access them directly, but through a recycler.
std::unordered_set<VKFramebuffer*> framebuffers;
std::unordered_set<VKBuffer*> buffers;
std::unordered_set<VKTexture*> textures;
std::unordered_set<VKQuery*> queries;
void perFrameCleanup();
// Called by the destructor
void beforeShutdownCleanup();
// Logical device, application's view of the physical device (GPU)
// VkPipeline cache object
VkPipelineCache _pipelineCache;
@ -429,7 +476,9 @@ protected:
vks::Context& _context{ vks::Context::get() };
//VkQueue _graphicsQueue; //TODO: initialize from device
//VkQueue _transferQueue; //TODO: initialize from device
vks::Texture2D _defaultTexture;
std::shared_ptr<gpu::Texture> _defaultTexture;
VKTexture* _defaultTextureVk{ nullptr };
VkDescriptorImageInfo _defaultTextureImageInfo{};
friend class VKBuffer;
friend class VKFramebuffer;
VkCommandBuffer _currentCommandBuffer;

View file

@ -92,7 +92,8 @@ VKBuffer::~VKBuffer() {
Backend::bufferCount.decrement();
auto backend = _backend.lock();
if (backend) {
backend->trash(*this);
backend->recycler.trashVkBuffer(buffer);
backend->recycler.trashVmaAllocation(allocation);
}
}

View file

@ -241,6 +241,7 @@ void VKAttachmentTexture::createTexture(VKBackend &backend) {
}
VKAttachmentTexture::~VKAttachmentTexture() {
// VKTODO: Redo destructors for cleanup to happen on present thread
auto backend = _backend.lock();
auto device = backend->getContext().device->logicalDevice;
if (_vkImageView) {

View file

@ -95,6 +95,11 @@ namespace gpu {
class TextureTable;
using TextureTablePointer = std::shared_ptr<TextureTable>;
namespace vk {
class VKBackend;
class VKBuffer;
}
struct StereoState {
StereoState() {}
bool isStereo() const {
@ -127,6 +132,7 @@ namespace gpu {
GPUObject* getGPUObject() const { return _gpuObject.get(); }
friend class Backend;
friend class vk::VKBackend;
friend class Texture;
};
@ -150,10 +156,6 @@ namespace gpu {
class GLESBuffer;
}
namespace vk {
class VKBackend;
class VKBuffer;
}
}
#endif

View file

@ -88,6 +88,7 @@ LightingModel::LightingModel() {
}
});
_ambientFresnelLUT->setStoredMipFormat(_ambientFresnelLUT->getTexelFormat());
_ambientFresnelLUT->assignStoredMip(0, N_roughness * N_NdotV * sizeof(LUTVector::value_type), (const gpu::Byte*)lut.data());
}
#endif

View file

@ -193,13 +193,6 @@ void Context::createInstance() {
void Context::destroyContext() {
VK_CHECK_RESULT(vkQueueWaitIdle(graphicsQueue));
for (const auto& trash : dumpster) {
trash();
}
while (!recycler.empty()) {
recycle();
}
device.reset();
if (enableValidation) {
@ -235,7 +228,7 @@ void Context::destroyContext() {
return bestMatch;
}*/
void Context::trashCommandBuffers(const std::vector<VkCommandBuffer>& cmdBuffers, VkCommandPool commandPool) const {
/*void Context::trashCommandBuffers(const std::vector<VkCommandBuffer>& cmdBuffers, VkCommandPool commandPool) const {
Q_ASSERT(commandPool);
using DtorLambda = std::function<void(const std::vector<VkCommandBuffer>&)>;
@ -244,7 +237,7 @@ void Context::trashCommandBuffers(const std::vector<VkCommandBuffer>& cmdBuffers
vkFreeCommandBuffers(device->logicalDevice, commandPool, cmdBuffers.size(), cmdBuffers.data());
};
trashAll<VkCommandBuffer>(cmdBuffers, destructor);
}
}*/
void Context::emptyDumpster(VkFence fence) {
VoidLambdaList newDumpster;

View file

@ -131,22 +131,6 @@ public:
uint32_t findQueue(const VkQueueFlags& desiredFlags, const VkSurfaceKHR& presentSurface = VkSurfaceKHR()) const;
template <typename T>
void trash(T value, std::function<void(T t)> destructor = [](T t) { t.destroy(); }) const {
if (!value) {
return;
}
dumpster.push_back([=] { destructor(value); });
}
template <typename T>
void trashAll(const std::vector<T>& values, std::function<void(const std::vector<T>&)> destructor) const {
if (values.empty()) {
return;
}
dumpster.push_back([=] { destructor(values); });
}
//
// Convenience functions for trashing specific types. These functions know what kind of function
// call to make for destroying a given Vulkan object.

View file

@ -13,40 +13,6 @@
namespace vks
{
/**
* Map a memory range of this buffer. If successful, mapped points to the specified buffer range.
*
* @param size (Optional) Size of the memory range to map. Pass VK_WHOLE_SIZE to map the complete buffer range.
* @param offset (Optional) Byte offset from beginning
*
* @return VkResult of the buffer mapping call
*/
/*VkResult Buffer::map(VkDeviceSize size, VkDeviceSize offset)
{
#if VULKAN_USE_VMA
return vmaMapMemory(getAllocator(),allocation, &mapped);
#else
return vkMapMemory(device, memory, offset, size, 0, &mapped);
#endif
}*/
/**
* Unmap a mapped memory range
*
* @note Does not return a result as vkUnmapMemory can't fail
*/
/*void Buffer::unmap()
{
if (mapped)
{
#if VULKAN_USE_VMA
vmaUnmapMemory(getAllocator(), allocation);
#else
vkUnmapMemory(device, memory);
#endif
mapped = nullptr;
}
}*/
/**
* Attach the allocated memory block to the buffer
@ -122,88 +88,4 @@ namespace vks
return newBuffer;
}
/**
* Copies the specified data to the mapped buffer
*
* @param data Pointer to the data to copy
* @param size Size of the data to copy in machine units
*
*/
void Buffer::copyTo(void* data, VkDeviceSize size)
{
assert(mapped);
memcpy(mapped, data, size);
}
/**
* Flush a memory range of the buffer to make it visible to the device
*
* @note Only required for non-coherent memory
*
* @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range.
* @param offset (Optional) Byte offset from beginning
*
* @return VkResult of the flush call
*/
/*VkResult Buffer::flush(VkDeviceSize size, VkDeviceSize offset)
{
#if VULKAN_USE_VMA
vmaFlushAllocation(getAllocator(), allocation, offset, size);
return VK_SUCCESS;
#else
VkMappedMemoryRange mappedRange = {};
mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return vkFlushMappedMemoryRanges(device, 1, &mappedRange);
#endif
}*/
/**
* Invalidate a memory range of the buffer to make it visible to the host
*
* @note Only required for non-coherent memory
*
* @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range.
* @param offset (Optional) Byte offset from beginning
*
* @return VkResult of the invalidate call
*/
/*VkResult Buffer::invalidate(VkDeviceSize size, VkDeviceSize offset)
{
#if VULKAN_USE_VMA
vmaInvalidateAllocation(getAllocator(), allocation, offset, size);
return VK_SUCCESS;
#else
VkMappedMemoryRange mappedRange = {};
mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange);
#endif
}*/
/**
* Release all Vulkan resources held by this buffer
*/
void Buffer::destroy()
{
if (buffer)
{
vkDestroyBuffer(device, buffer, nullptr);
}
Allocation::destroy();
#if VULKAN_USE_VMA
if (allocation) {
vmaFreeMemory(getAllocator(), allocation);
}
#else
if (memory)
{
vkFreeMemory(device, memory, nullptr);
}
#endif
}
};

View file

@ -46,9 +46,7 @@ namespace vks
static std::shared_ptr<Buffer> createUniform(VkDeviceSize bufferSize);
static std::shared_ptr<Buffer> createStorage(VkDeviceSize bufferSize);
static std::shared_ptr<Buffer> createVertex(VkDeviceSize bufferSize);
void copyTo(void* data, VkDeviceSize size);
//VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
//VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
void destroy() override;
};
}

View file

@ -28,308 +28,6 @@ namespace vks
vkFreeMemory(device->logicalDevice, deviceMemory, nullptr);
}
/*ktxResult Texture::loadKTXFile(std::string filename, ktxTexture **target)
{
ktxResult result = KTX_SUCCESS;
#if defined(__ANDROID__)
AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
if (!asset) {
vks::tools::exitFatal("Could not load texture from " + filename + "\n\nMake sure the assets submodule has been checked out and is up-to-date.", -1);
}
size_t size = AAsset_getLength(asset);
assert(size > 0);
ktx_uint8_t *textureData = new ktx_uint8_t[size];
AAsset_read(asset, textureData, size);
AAsset_close(asset);
result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target);
delete[] textureData;
#else
if (!vks::tools::fileExists(filename)) {
vks::tools::exitFatal("Could not load texture from " + filename + "\n\nMake sure the assets submodule has been checked out and is up-to-date.", -1);
}
result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target);
#endif
return result;
}*/
/**
* Load a 2D texture including all mip levels
*
* @param filename File to load (supports .ktx)
* @param format Vulkan format of the image data stored in the file
* @param device Vulkan device to create the texture on
* @param copyQueue Queue used for the texture staging copy commands (must support transfer)
* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
* @param (Optional) forceLinear Force linear tiling (not advised, defaults to false)
*
*/
/*void Texture2D::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, bool forceLinear)
{
ktxTexture* ktxTexture;
ktxResult result = loadKTXFile(filename, &ktxTexture);
assert(result == KTX_SUCCESS);
this->device = device;
width = ktxTexture->baseWidth;
height = ktxTexture->baseHeight;
mipLevels = ktxTexture->numLevels;
ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
// Get device properties for the requested texture format
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties);
// Only use linear tiling if requested (and supported by the device)
// Support for linear tiling is mostly limited, so prefer to use
// optimal tiling instead
// On most implementations linear tiling will only support a very
// limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
VkBool32 useStaging = !forceLinear;
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
VkMemoryRequirements memReqs;
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
if (useStaging)
{
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer;
VkDeviceMemory stagingMemory;
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
bufferCreateInfo.size = ktxTextureSize;
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);
memAllocInfo.allocationSize = memReqs.size;
// Get memory type index for a host visible buffer
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
// Copy texture data into staging buffer
uint8_t *data;
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
memcpy(data, ktxTextureData, ktxTextureSize);
vkUnmapMemory(device->logicalDevice, stagingMemory);
// Setup buffer copy regions for each mip level
std::vector<VkBufferImageCopy> bufferCopyRegions;
for (uint32_t i = 0; i < mipLevels; i++)
{
ktx_size_t offset;
KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset);
assert(result == KTX_SUCCESS);
VkBufferImageCopy bufferCopyRegion = {};
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
bufferCopyRegion.imageSubresource.mipLevel = i;
bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
bufferCopyRegion.imageSubresource.layerCount = 1;
bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i);
bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i);
bufferCopyRegion.imageExtent.depth = 1;
bufferCopyRegion.bufferOffset = offset;
bufferCopyRegions.push_back(bufferCopyRegion);
}
// Create optimal tiled target image
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = format;
imageCreateInfo.mipLevels = mipLevels;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCreateInfo.extent = { width, height, 1 };
imageCreateInfo.usage = imageUsageFlags;
// Ensure that the TRANSFER_DST bit is set for staging
if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
{
imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs);
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory));
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));
VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = mipLevels;
subresourceRange.layerCount = 1;
// Image barrier for optimal image (target)
// Optimal image will be used as destination for the copy
vks::tools::setImageLayout(
copyCmd,
image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresourceRange);
// Copy mip levels from staging buffer
vkCmdCopyBufferToImage(
copyCmd,
stagingBuffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
static_cast<uint32_t>(bufferCopyRegions.size()),
bufferCopyRegions.data()
);
// Change texture image layout to shader read after all mip levels have been copied
this->imageLayout = imageLayout;
vks::tools::setImageLayout(
copyCmd,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
imageLayout,
subresourceRange);
device->flushCommandBuffer(copyCmd, copyQueue);
// Clean up staging resources
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
}
else
{
// Prefer using optimal tiling, as linear tiling
// may support only a small set of features
// depending on implementation (e.g. no mip maps, only one layer, etc.)
// Check if this support is supported for linear tiling
assert(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
VkImage mappableImage;
VkDeviceMemory mappableMemory;
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = format;
imageCreateInfo.extent = { width, height, 1 };
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imageCreateInfo.usage = imageUsageFlags;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
// Load mip map level 0 to linear tiling image
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &mappableImage));
// Get memory requirements for this image
// like size and alignment
vkGetImageMemoryRequirements(device->logicalDevice, mappableImage, &memReqs);
// Set memory allocation size to required memory size
memAllocInfo.allocationSize = memReqs.size;
// Get memory type that can be mapped to host memory
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
// Allocate host memory
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &mappableMemory));
// Bind allocated image for use
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, mappableImage, mappableMemory, 0));
// Get sub resource layout
// Mip map count, array layer, etc.
VkImageSubresource subRes = {};
subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subRes.mipLevel = 0;
VkSubresourceLayout subResLayout;
void *data;
// Get sub resources layout
// Includes row pitch, size offsets, etc.
vkGetImageSubresourceLayout(device->logicalDevice, mappableImage, &subRes, &subResLayout);
// Map image memory
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, mappableMemory, 0, memReqs.size, 0, &data));
// Copy image data into memory
memcpy(data, ktxTextureData, memReqs.size);
vkUnmapMemory(device->logicalDevice, mappableMemory);
// Linear tiled images don't need to be staged
// and can be directly used as textures
image = mappableImage;
deviceMemory = mappableMemory;
this->imageLayout = imageLayout;
// Setup image memory barrier
vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout);
device->flushCommandBuffer(copyCmd, copyQueue);
}
ktxTexture_Destroy(ktxTexture);
// Create a default sampler
VkSamplerCreateInfo samplerCreateInfo = {};
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerCreateInfo.mipLodBias = 0.0f;
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
samplerCreateInfo.minLod = 0.0f;
// Max level-of-detail should match mip level count
samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f;
// Only enable anisotropic filtering if enabled on the device
samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f;
samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy;
samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));
// Create image view
// Textures are not directly accessed by the shaders and
// are abstracted by image views containing additional
// information and sub resource ranges
VkImageViewCreateInfo viewCreateInfo = {};
viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewCreateInfo.format = format;
viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
// Linear tiling usually won't support mip maps
// Only set mip map count if optimal tiling is used
viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1;
viewCreateInfo.image = image;
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
// Update descriptor image info member that can be used for setting up descriptor sets
updateDescriptor();
}*/
/**
* Creates a 2D texture from a buffer
*
@ -506,364 +204,5 @@ namespace vks
* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
*
*/
/*void Texture2DArray::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout)
{
ktxTexture* ktxTexture;
ktxResult result = loadKTXFile(filename, &ktxTexture);
assert(result == KTX_SUCCESS);
this->device = device;
width = ktxTexture->baseWidth;
height = ktxTexture->baseHeight;
layerCount = ktxTexture->numLayers;
mipLevels = ktxTexture->numLevels;
ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
VkMemoryRequirements memReqs;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer;
VkDeviceMemory stagingMemory;
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
bufferCreateInfo.size = ktxTextureSize;
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);
memAllocInfo.allocationSize = memReqs.size;
// Get memory type index for a host visible buffer
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
// Copy texture data into staging buffer
uint8_t *data;
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
memcpy(data, ktxTextureData, ktxTextureSize);
vkUnmapMemory(device->logicalDevice, stagingMemory);
// Setup buffer copy regions for each layer including all of its miplevels
std::vector<VkBufferImageCopy> bufferCopyRegions;
for (uint32_t layer = 0; layer < layerCount; layer++)
{
for (uint32_t level = 0; level < mipLevels; level++)
{
ktx_size_t offset;
KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, layer, 0, &offset);
assert(result == KTX_SUCCESS);
VkBufferImageCopy bufferCopyRegion = {};
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
bufferCopyRegion.imageSubresource.mipLevel = level;
bufferCopyRegion.imageSubresource.baseArrayLayer = layer;
bufferCopyRegion.imageSubresource.layerCount = 1;
bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level;
bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level;
bufferCopyRegion.imageExtent.depth = 1;
bufferCopyRegion.bufferOffset = offset;
bufferCopyRegions.push_back(bufferCopyRegion);
}
}
// Create optimal tiled target image
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = format;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCreateInfo.extent = { width, height, 1 };
imageCreateInfo.usage = imageUsageFlags;
// Ensure that the TRANSFER_DST bit is set for staging
if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
{
imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
imageCreateInfo.arrayLayers = layerCount;
imageCreateInfo.mipLevels = mipLevels;
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs);
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory));
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
// Image barrier for optimal image (target)
// Set initial layout for all array layers (faces) of the optimal (target) tiled texture
VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = mipLevels;
subresourceRange.layerCount = layerCount;
vks::tools::setImageLayout(
copyCmd,
image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresourceRange);
// Copy the layers and mip levels from the staging buffer to the optimal tiled image
vkCmdCopyBufferToImage(
copyCmd,
stagingBuffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
static_cast<uint32_t>(bufferCopyRegions.size()),
bufferCopyRegions.data());
// Change texture image layout to shader read after all faces have been copied
this->imageLayout = imageLayout;
vks::tools::setImageLayout(
copyCmd,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
imageLayout,
subresourceRange);
device->flushCommandBuffer(copyCmd, copyQueue);
// Create sampler
VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo();
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU;
samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU;
samplerCreateInfo.mipLodBias = 0.0f;
samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f;
samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy;
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
samplerCreateInfo.minLod = 0.0f;
samplerCreateInfo.maxLod = (float)mipLevels;
samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));
// Create image view
VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo();
viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewCreateInfo.format = format;
viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
viewCreateInfo.subresourceRange.layerCount = layerCount;
viewCreateInfo.subresourceRange.levelCount = mipLevels;
viewCreateInfo.image = image;
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
// Clean up staging resources
ktxTexture_Destroy(ktxTexture);
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
// Update descriptor image info member that can be used for setting up descriptor sets
updateDescriptor();
}*/
/**
* Load a cubemap texture including all mip levels from a single file
*
* @param filename File to load (supports .ktx)
* @param format Vulkan format of the image data stored in the file
* @param device Vulkan device to create the texture on
* @param copyQueue Queue used for the texture staging copy commands (must support transfer)
* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
*
*/
/*void TextureCubeMap::loadFromFile(std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout)
{
ktxTexture* ktxTexture;
ktxResult result = loadKTXFile(filename, &ktxTexture);
assert(result == KTX_SUCCESS);
this->device = device;
width = ktxTexture->baseWidth;
height = ktxTexture->baseHeight;
mipLevels = ktxTexture->numLevels;
ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
VkMemoryRequirements memReqs;
// Create a host-visible staging buffer that contains the raw image data
VkBuffer stagingBuffer;
VkDeviceMemory stagingMemory;
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
bufferCreateInfo.size = ktxTextureSize;
// This buffer is used as a transfer source for the buffer copy
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));
// Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);
memAllocInfo.allocationSize = memReqs.size;
// Get memory type index for a host visible buffer
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
// Copy texture data into staging buffer
uint8_t *data;
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
memcpy(data, ktxTextureData, ktxTextureSize);
vkUnmapMemory(device->logicalDevice, stagingMemory);
// Setup buffer copy regions for each face including all of its mip levels
std::vector<VkBufferImageCopy> bufferCopyRegions;
for (uint32_t face = 0; face < 6; face++)
{
for (uint32_t level = 0; level < mipLevels; level++)
{
ktx_size_t offset;
KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, 0, face, &offset);
assert(result == KTX_SUCCESS);
VkBufferImageCopy bufferCopyRegion = {};
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
bufferCopyRegion.imageSubresource.mipLevel = level;
bufferCopyRegion.imageSubresource.baseArrayLayer = face;
bufferCopyRegion.imageSubresource.layerCount = 1;
bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level;
bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level;
bufferCopyRegion.imageExtent.depth = 1;
bufferCopyRegion.bufferOffset = offset;
bufferCopyRegions.push_back(bufferCopyRegion);
}
}
// Create optimal tiled target image
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = format;
imageCreateInfo.mipLevels = mipLevels;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCreateInfo.extent = { width, height, 1 };
imageCreateInfo.usage = imageUsageFlags;
// Ensure that the TRANSFER_DST bit is set for staging
if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
{
imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
// Cube faces count as array layers in Vulkan
imageCreateInfo.arrayLayers = 6;
// This flag is required for cube map images
imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
vkGetImageMemoryRequirements(device->logicalDevice, image, &memReqs);
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &deviceMemory));
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));
// Use a separate command buffer for texture loading
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
// Image barrier for optimal image (target)
// Set initial layout for all array layers (faces) of the optimal (target) tiled texture
VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = mipLevels;
subresourceRange.layerCount = 6;
vks::tools::setImageLayout(
copyCmd,
image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresourceRange);
// Copy the cube map faces from the staging buffer to the optimal tiled image
vkCmdCopyBufferToImage(
copyCmd,
stagingBuffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
static_cast<uint32_t>(bufferCopyRegions.size()),
bufferCopyRegions.data());
// Change texture image layout to shader read after all faces have been copied
this->imageLayout = imageLayout;
vks::tools::setImageLayout(
copyCmd,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
imageLayout,
subresourceRange);
device->flushCommandBuffer(copyCmd, copyQueue);
// Create sampler
VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo();
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU;
samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU;
samplerCreateInfo.mipLodBias = 0.0f;
samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f;
samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy;
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
samplerCreateInfo.minLod = 0.0f;
samplerCreateInfo.maxLod = (float)mipLevels;
samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &sampler));
// Create image view
VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo();
viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
viewCreateInfo.format = format;
viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
viewCreateInfo.subresourceRange.layerCount = 6;
viewCreateInfo.subresourceRange.levelCount = mipLevels;
viewCreateInfo.image = image;
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
// Clean up staging resources
ktxTexture_Destroy(ktxTexture);
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
// Update descriptor image info member that can be used for setting up descriptor sets
updateDescriptor();
}*/
}

View file

@ -51,14 +51,6 @@ class Texture
class Texture2D : public Texture
{
public:
/*void loadFromFile(
std::string filename,
VkFormat format,
vks::VulkanDevice *device,
VkQueue copyQueue,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
bool forceLinear = false);*/
void fromBuffer(
void * buffer,
VkDeviceSize bufferSize,
@ -71,28 +63,4 @@ class Texture2D : public Texture
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
};
class Texture2DArray : public Texture
{
public:
/*void loadFromFile(
std::string filename,
VkFormat format,
vks::VulkanDevice *device,
VkQueue copyQueue,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);*/
};
class TextureCubeMap : public Texture
{
public:
/*void loadFromFile(
std::string filename,
VkFormat format,
vks::VulkanDevice *device,
VkQueue copyQueue,
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);*/
};
} // namespace vks

View file

@ -357,7 +357,7 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) {
vkBackend->waitForGPU();
vkBackend->recycleFrame();
_vkcontext.emptyDumpster(frameFence);
_vkcontext.recycle();
//_vkcontext.recycle();
if (frame && !frame->batches.empty()) {
if (rdoc_api)
rdoc_api->EndFrameCapture(NULL, NULL);
@ -406,11 +406,11 @@ bool RenderThread::process() {
void RenderThread::setupFramebuffers() {
// Recreate the frame buffers
_vkcontext.trashAll<VkFramebuffer>(_framebuffers, [this](const std::vector<VkFramebuffer>& framebuffers) {
for (const auto& framebuffer : framebuffers) {
vkDestroyFramebuffer(_vkcontext.device->logicalDevice, framebuffer, nullptr);
}
});
auto vkBackend = std::dynamic_pointer_cast<gpu::vk::VKBackend>(_gpuContext->getBackend());
for (auto framebuffer : _framebuffers) {
vkBackend->recycler.trashVkFramebuffer(framebuffer);
}
_framebuffers.clear();
std::vector<VkImageView> attachments;
attachments.resize(1);