| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "BsVulkanGpuParams.h"
- #include "BsVulkanUtility.h"
- #include "BsVulkanRenderAPI.h"
- #include "BsVulkanDevice.h"
- #include "BsVulkanGpuParamBlockBuffer.h"
- #include "BsVulkanGpuBuffer.h"
- #include "BsVulkanTexture.h"
- #include "BsVulkanHardwareBuffer.h"
- #include "BsVulkanDescriptorSet.h"
- #include "BsVulkanDescriptorLayout.h"
- #include "BsVulkanSamplerState.h"
- #include "BsVulkanGpuPipelineParamInfo.h"
- #include "BsVulkanCommandBuffer.h"
- #include "BsGpuParamDesc.h"
- namespace BansheeEngine
- {
- VulkanGpuParams::VulkanGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask)
- : GpuParamsCore(paramInfo, deviceMask), mPerDeviceData(), mDeviceMask(deviceMask), mData(nullptr), mSetsDirty(nullptr)
- {
-
- }
- VulkanGpuParams::~VulkanGpuParams()
- {
- {
- Lock lock(mMutex);
- UINT32 numSets = mParamInfo->getNumSets();
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- for (UINT32 j = 0; j < numSets; j++)
- {
- for (auto& entry : mPerDeviceData[i].perSetData[j].sets)
- entry->destroy();
- }
- }
- }
- bs_free(mData); // Everything allocated under a single buffer to a single free is enough
- }
- void VulkanGpuParams::initialize()
- {
- VulkanGpuPipelineParamInfo& vkParamInfo = static_cast<VulkanGpuPipelineParamInfo&>(*mParamInfo);
- VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
- VulkanDevice* devices[BS_MAX_DEVICES];
- VulkanUtility::getDevices(rapi, mDeviceMask, devices);
- UINT32 numDevices = 0;
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (devices != nullptr)
- numDevices++;
- }
- UINT32 numSets = vkParamInfo.getNumSets();
- UINT32 numBindings = vkParamInfo.getNumElements();
- // Note: I'm assuming a single WriteInfo per binding, but if arrays sizes larger than 1 are eventually supported
- // I'll need to adjust the code.
- UINT32 setsDirtyBytes = sizeof(bool) * numSets;
- UINT32 perSetBytes = sizeof(PerSetData) * numSets;
- UINT32 writeSetInfosBytes = sizeof(VkWriteDescriptorSet) * numBindings;
- UINT32 writeInfosBytes = sizeof(WriteInfo) * numBindings;
- mData = (UINT8*)bs_alloc(setsDirtyBytes + (perSetBytes + writeSetInfosBytes + writeInfosBytes) * numDevices);
- UINT8* dataIter = mData;
- Lock lock(mMutex); // Set write operations need to be thread safe
- mSetsDirty = (bool*)dataIter;
- memset(mSetsDirty, 1, setsDirtyBytes);
- dataIter += setsDirtyBytes;
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (devices[i] == nullptr)
- {
- mPerDeviceData[i].perSetData = nullptr;
- continue;
- }
- mPerDeviceData[i].perSetData = (PerSetData*)dataIter;
- dataIter += sizeof(perSetBytes);
- VulkanDescriptorManager& descManager = devices[i]->getDescriptorManager();
- for (UINT32 j = 0; j < numSets; j++)
- {
- UINT32 numBindingsPerSet = vkParamInfo.getNumBindings(j);
- PerSetData& perSetData = mPerDeviceData[i].perSetData[j];
- perSetData.writeSetInfos = (VkWriteDescriptorSet*)dataIter;
- dataIter += sizeof(VkWriteDescriptorSet) * numBindingsPerSet;
- perSetData.writeInfos = (WriteInfo*)dataIter;
- dataIter += sizeof(WriteInfo) * numBindingsPerSet;
- VulkanDescriptorLayout* layout = vkParamInfo.getLayout(i, j);
- perSetData.numElements = numBindingsPerSet;
- perSetData.latestSet = descManager.createSet(layout);
- perSetData.sets.push_back(perSetData.latestSet);
- VkDescriptorSetLayoutBinding* perSetBindings = vkParamInfo.getBindings(j);
- for (UINT32 k = 0; k < numBindingsPerSet; k++)
- {
- // Note: Instead of using one structure per binding, it's possible to update multiple at once
- // by specifying larger descriptorCount, if they all share type and shader stages.
- VkWriteDescriptorSet& writeSetInfo = perSetData.writeSetInfos[k];
- writeSetInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- writeSetInfo.pNext = nullptr;
- writeSetInfo.dstSet = VK_NULL_HANDLE;
- writeSetInfo.dstBinding = perSetBindings[k].binding;
- writeSetInfo.dstArrayElement = 0;
- writeSetInfo.descriptorCount = perSetBindings[k].descriptorCount;
- writeSetInfo.descriptorType = perSetBindings[k].descriptorType;
- bool isImage = writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ||
- writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ||
- writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
- if (isImage)
- {
- bool isLoadStore = writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
- VkDescriptorImageInfo& imageInfo = perSetData.writeInfos[k].image;
- imageInfo.sampler = VK_NULL_HANDLE;
- imageInfo.imageView = VK_NULL_HANDLE;
- imageInfo.imageLayout = isLoadStore ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- writeSetInfo.pImageInfo = &imageInfo;
- writeSetInfo.pBufferInfo = nullptr;
- writeSetInfo.pTexelBufferView = nullptr;
- }
- else
- {
- bool isLoadStore = writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
- if (!isLoadStore)
- {
- VkDescriptorBufferInfo& bufferInfo = perSetData.writeInfos[k].buffer;
- bufferInfo.buffer = VK_NULL_HANDLE;
- bufferInfo.offset = 0;
- bufferInfo.range = VK_WHOLE_SIZE;
- writeSetInfo.pBufferInfo = &bufferInfo;
- }
- else
- writeSetInfo.pBufferInfo = nullptr;
- writeSetInfo.pTexelBufferView = nullptr;
- writeSetInfo.pImageInfo = nullptr;
- }
- }
- }
- }
- GpuParamsCore::initialize();
- }
- void VulkanGpuParams::setParamBlockBuffer(UINT32 set, UINT32 slot, const SPtr<GpuParamBlockBufferCore>& paramBlockBuffer)
- {
- GpuParamsCore::setParamBlockBuffer(set, slot, paramBlockBuffer);
- Lock(mMutex);
- VulkanGpuParamBlockBufferCore* vulkanParamBlockBuffer =
- static_cast<VulkanGpuParamBlockBufferCore*>(paramBlockBuffer.get());
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (mPerDeviceData[i].perSetData == nullptr)
- continue;
- VulkanBuffer* bufferRes = vulkanParamBlockBuffer->getResource(i);
- if (bufferRes != nullptr)
- mPerDeviceData[i].perSetData[set].writeInfos[slot].buffer.buffer = bufferRes->getHandle();
- else
- mPerDeviceData[i].perSetData[set].writeInfos[slot].buffer.buffer = VK_NULL_HANDLE;
- }
- mSetsDirty[set] = true;
- }
- void VulkanGpuParams::setTexture(UINT32 set, UINT32 slot, const SPtr<TextureCore>& texture)
- {
- GpuParamsCore::setTexture(set, slot, texture);
- Lock(mMutex);
- VulkanTextureCore* vulkanTexture = static_cast<VulkanTextureCore*>(texture.get());
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (mPerDeviceData[i].perSetData == nullptr)
- continue;
- mPerDeviceData[i].perSetData[set].writeInfos[slot].image.imageView = vulkanTexture->getView(i);
- }
- mSetsDirty[set] = true;
- }
- void VulkanGpuParams::setLoadStoreTexture(UINT32 set, UINT32 slot, const SPtr<TextureCore>& texture,
- const TextureSurface& surface)
- {
- GpuParamsCore::setLoadStoreTexture(set, slot, texture, surface);
- Lock(mMutex);
- VulkanTextureCore* vulkanTexture = static_cast<VulkanTextureCore*>(texture.get());
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (mPerDeviceData[i].perSetData == nullptr)
- continue;
- mPerDeviceData[i].perSetData[set].writeInfos[slot].image.imageView = vulkanTexture->getView(i, surface);
- }
- mSetsDirty[set] = true;
- }
- void VulkanGpuParams::setBuffer(UINT32 set, UINT32 slot, const SPtr<GpuBufferCore>& buffer)
- {
- GpuParamsCore::setBuffer(set, slot, buffer);
- Lock(mMutex);
- VulkanGpuBufferCore* vulkanBuffer = static_cast<VulkanGpuBufferCore*>(buffer.get());
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (mPerDeviceData[i].perSetData == nullptr)
- continue;
- VulkanBuffer* bufferRes = vulkanBuffer->getResource(i);
- if (bufferRes != nullptr)
- mPerDeviceData[i].perSetData[set].writeInfos[slot].bufferView = bufferRes->getView();
- else
- mPerDeviceData[i].perSetData[set].writeInfos[slot].bufferView = VK_NULL_HANDLE;
- }
- mSetsDirty[set] = true;
- }
- void VulkanGpuParams::setSamplerState(UINT32 set, UINT32 slot, const SPtr<SamplerStateCore>& sampler)
- {
- GpuParamsCore::setSamplerState(set, slot, sampler);
- Lock(mMutex);
- VulkanSamplerStateCore* vulkanSampler = static_cast<VulkanSamplerStateCore*>(sampler.get());
- for(UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (mPerDeviceData[i].perSetData == nullptr)
- continue;
- VulkanSampler* samplerRes = vulkanSampler->getResource(i);
- if (samplerRes != nullptr)
- mPerDeviceData[i].perSetData[set].writeInfos[slot].image.sampler = samplerRes->getHandle();
- else
- mPerDeviceData[i].perSetData[set].writeInfos[slot].image.sampler = VK_NULL_HANDLE;
- }
- mSetsDirty[set] = true;
- }
- void VulkanGpuParams::setLoadStoreSurface(UINT32 set, UINT32 slot, const TextureSurface& surface)
- {
- GpuParamsCore::setLoadStoreSurface(set, slot, surface);
- SPtr<TextureCore> texture = getLoadStoreTexture(set, slot);
- if (texture == nullptr)
- return;
- Lock(mMutex);
- VulkanTextureCore* vulkanTexture = static_cast<VulkanTextureCore*>(texture.get());
- for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
- {
- if (mPerDeviceData[i].perSetData == nullptr)
- continue;
- mPerDeviceData[i].perSetData[set].writeInfos[slot].image.imageView = vulkanTexture->getView(i, surface);
- }
- mSetsDirty[set] = true;
- }
- void VulkanGpuParams::bind(VulkanCommandBuffer& buffer)
- {
- UINT32 deviceIdx = buffer.getDeviceIdx();
- PerDeviceData& perDeviceData = mPerDeviceData[deviceIdx];
- if (perDeviceData.perSetData == nullptr)
- return;
- UINT32 numParamBlocks = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::ParamBlock);
- UINT32 numTextures = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::Texture);
- UINT32 numStorageTextures = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::LoadStoreTexture);
- UINT32 numBuffers = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::Buffer);
- UINT32 numSamplers = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::SamplerState);
- UINT32 numSets = mParamInfo->getNumSets();
- // Registers resources with the command buffer
- // Note: Makes the assumption that this object (and all of the resources it holds) are externally locked, and will
- // not be modified on another thread while being bound.
- VulkanCmdBuffer* internalCB = buffer.getInternal();
- for (UINT32 i = 0; i < numParamBlocks; i++)
- {
- if (mParamBlockBuffers[i] == nullptr)
- continue;
- VulkanGpuParamBlockBufferCore* element = static_cast<VulkanGpuParamBlockBufferCore*>(mParamBlockBuffers[i].get());
- VulkanBuffer* resource = element->getResource(deviceIdx);
- internalCB->registerResource(resource, VK_ACCESS_UNIFORM_READ_BIT, VulkanUseFlag::Read);
- }
- for (UINT32 i = 0; i < numBuffers; i++)
- {
- if (mBuffers[i] == nullptr)
- continue;
- VulkanGpuBufferCore* element = static_cast<VulkanGpuBufferCore*>(mBuffers[i].get());
- VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT;
- VulkanUseFlags useFlags = VulkanUseFlag::Read;
- if (element->getProperties().getRandomGpuWrite())
- {
- accessFlags |= VK_ACCESS_SHADER_WRITE_BIT;
- useFlags |= VulkanUseFlag::Write;
- }
- VulkanBuffer* resource = element->getResource(deviceIdx);
- internalCB->registerResource(resource, accessFlags, useFlags);
- }
- for (UINT32 i = 0; i < numSamplers; i++)
- {
- if (mSamplerStates[i] == nullptr)
- continue;
- VulkanSamplerStateCore* element = static_cast<VulkanSamplerStateCore*>(mSamplerStates[i].get());
- VulkanSampler* resource = element->getResource(deviceIdx);
- if (resource == nullptr)
- continue;
- internalCB->registerResource(resource, VulkanUseFlag::Read);
- }
- for (UINT32 i = 0; i < numStorageTextures; i++)
- {
- if (mLoadStoreTextures[i] == nullptr)
- continue;
- VulkanTextureCore* element = static_cast<VulkanTextureCore*>(mLoadStoreTextures[i].get());
- VulkanImage* resource = element->getResource(deviceIdx);
- if (resource == nullptr)
- continue;
- VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
- VulkanUseFlags useFlags = VulkanUseFlag::Read | VulkanUseFlag::Write;
- const TextureSurface& surface = mLoadStoreSurfaces[i];
- VkImageSubresourceRange range;
- range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- range.baseArrayLayer = surface.arraySlice;
- range.layerCount = surface.numArraySlices;
- range.baseMipLevel = surface.mipLevel;
- range.levelCount = surface.numMipLevels;
- internalCB->registerResource(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, range, useFlags);
- }
- for (UINT32 i = 0; i < numTextures; i++)
- {
- if (mTextures[i] == nullptr)
- continue;
- VulkanTextureCore* element = static_cast<VulkanTextureCore*>(mTextures[i].get());
- VulkanImage* resource = element->getResource(deviceIdx);
- if (resource == nullptr)
- continue;
- const TextureProperties& props = element->getProperties();
- bool isDepthStencil = (props.getUsage() & TU_DEPTHSTENCIL) != 0;
- VkImageSubresourceRange range;
- range.aspectMask = isDepthStencil ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
- : VK_IMAGE_ASPECT_COLOR_BIT;
- range.baseArrayLayer = 0;
- range.layerCount = props.getNumFaces();
- range.baseMipLevel = 0;
- range.levelCount = props.getNumMipmaps();
- internalCB->registerResource(resource, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
- range, VulkanUseFlag::Read);
- }
- // Acquire sets as needed, and updated their contents if dirty
- VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
- VulkanDevice& device = *rapi._getDevice(deviceIdx);
- VulkanDescriptorManager& descManager = device.getDescriptorManager();
- VulkanGpuPipelineParamInfo& vkParamInfo = static_cast<VulkanGpuPipelineParamInfo&>(*mParamInfo);
- Lock(mMutex);
- for (UINT32 i = 0; i < numSets; i++)
- {
- PerSetData& perSetData = perDeviceData.perSetData[i];
- if (!mSetsDirty[i]) // Set not dirty, just use the last one we wrote (this is fine even across multiple command buffers)
- continue;
- // Set is dirty, we need to update
- //// Use latest unless already used, otherwise try to find an unused one
- if(perSetData.latestSet->isBound()) // Checking this is okay, because it's only modified below when we call registerResource, which is under the same lock as this
- {
- perSetData.latestSet = nullptr;
- for(auto& entry : perSetData.sets)
- {
- if(!entry->isBound())
- {
- perSetData.latestSet = entry;
- break;
- }
- }
- // Cannot find an empty set, allocate a new one
- VulkanDescriptorLayout* layout = vkParamInfo.getLayout(deviceIdx, i);
- perSetData.latestSet = descManager.createSet(layout);
- perSetData.sets.push_back(perSetData.latestSet);
- }
- // Note: Currently I write to the entire set at once, but it might be beneficial to remember only the exact
- // entries that were updated, and only write to them individually.
- perSetData.latestSet->write(perSetData.writeSetInfos, perSetData.numElements);
- mSetsDirty[i] = false;
- }
- // Actually bind the sets to the command buffer
- VkCommandBuffer vkCB = internalCB->getHandle();
- VkPipelineBindPoint bindPoint;
- GpuQueueType queueType = buffer.getType();
- switch(queueType)
- {
- case GQT_GRAPHICS:
- bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- break;
- case GQT_COMPUTE:
- bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
- break;
- case GQT_UPLOAD:
- default:
- LOGERR("Cannot bind GpuParams on the upload queue. Ignoring.");
- return;
- }
- VkDescriptorSet* sets = bs_stack_alloc<VkDescriptorSet>(numSets);
- for (UINT32 i = 0; i < numSets; i++)
- {
- VulkanDescriptorSet* set = perDeviceData.perSetData[i].latestSet;
- internalCB->registerResource(set, VulkanUseFlag::Read);
- sets[i] = set->getHandle();
- }
- vkCmdBindDescriptorSets(vkCB, bindPoint, perDeviceData.pipelineLayout, 0,
- numSets, sets, 0, nullptr);
- bs_stack_free(sets);
- }
- }
|