Fix UsedSRsMask/UsedUAsMask when binding arrays to the shader
This commit is contained in:
@@ -12,7 +12,7 @@ class GPUShaderProgram;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The runtime version of the shaders cache supported by the all graphics back-ends. The same for all the shader cache formats (easier to sync and validate).
|
/// The runtime version of the shaders cache supported by the all graphics back-ends. The same for all the shader cache formats (easier to sync and validate).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#define GPU_SHADER_CACHE_VERSION 8
|
#define GPU_SHADER_CACHE_VERSION 9
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents collection of shader programs with permutations and custom names.
|
/// Represents collection of shader programs with permutations and custom names.
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "GPUDeviceVulkan.h"
|
#include "GPUDeviceVulkan.h"
|
||||||
#include "RenderToolsVulkan.h"
|
#include "RenderToolsVulkan.h"
|
||||||
#include "GPUContextVulkan.h"
|
#include "GPUContextVulkan.h"
|
||||||
#include "GPUAdapterVulkan.h"
|
|
||||||
#include "CmdBufferVulkan.h"
|
#include "CmdBufferVulkan.h"
|
||||||
#include "Engine/Threading/Threading.h"
|
#include "Engine/Threading/Threading.h"
|
||||||
#include "Engine/Engine/Engine.h"
|
#include "Engine/Engine/Engine.h"
|
||||||
@@ -30,39 +29,48 @@ void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
|
|||||||
_typesUsageID = id;
|
_typesUsageID = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DescriptorSetLayoutInfoVulkan::AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor)
|
|
||||||
{
|
|
||||||
_layoutTypes[descriptor.descriptorType]++;
|
|
||||||
|
|
||||||
if (descriptorSetIndex >= _setLayouts.Count())
|
|
||||||
{
|
|
||||||
_setLayouts.Resize(descriptorSetIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetLayout& descSetLayout = _setLayouts[descriptorSetIndex];
|
|
||||||
descSetLayout.LayoutBindings.Add(descriptor);
|
|
||||||
|
|
||||||
_hash = Crc::MemCrc32(&descriptor, sizeof(descriptor), _hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits stageFlags, DescriptorSet::Stage descSet, const SpirvShaderDescriptorInfo* descriptorInfo)
|
void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits stageFlags, DescriptorSet::Stage descSet, const SpirvShaderDescriptorInfo* descriptorInfo)
|
||||||
{
|
{
|
||||||
const int32 descriptorSetIndex = (int32)descSet;
|
const int32 descriptorSetIndex = descSet;
|
||||||
|
if (descriptorSetIndex >= _setLayouts.Count())
|
||||||
|
_setLayouts.Resize(descriptorSetIndex + 1);
|
||||||
|
SetLayout& descSetLayout = _setLayouts[descriptorSetIndex];
|
||||||
|
|
||||||
VkDescriptorSetLayoutBinding binding;
|
VkDescriptorSetLayoutBinding binding;
|
||||||
binding.descriptorCount = 1;
|
|
||||||
binding.stageFlags = stageFlags;
|
binding.stageFlags = stageFlags;
|
||||||
binding.pImmutableSamplers = nullptr;
|
binding.pImmutableSamplers = nullptr;
|
||||||
|
|
||||||
for (uint32 descriptorIndex = 0; descriptorIndex < descriptorInfo->DescriptorTypesCount; descriptorIndex++)
|
for (uint32 descriptorIndex = 0; descriptorIndex < descriptorInfo->DescriptorTypesCount; descriptorIndex++)
|
||||||
{
|
{
|
||||||
auto& descriptor = descriptorInfo->DescriptorTypes[descriptorIndex];
|
auto& descriptor = descriptorInfo->DescriptorTypes[descriptorIndex];
|
||||||
binding.binding = descriptorIndex;
|
binding.binding = descriptorIndex;
|
||||||
binding.descriptorType = descriptor.DescriptorType;
|
binding.descriptorType = descriptor.DescriptorType;
|
||||||
AddDescriptor(descriptorSetIndex, binding);
|
binding.descriptorCount = descriptor.Count;
|
||||||
|
|
||||||
|
_layoutTypes[binding.descriptorType]++;
|
||||||
|
descSetLayout.LayoutBindings.Add(binding);
|
||||||
|
_hash = Crc::MemCrc32(&binding, sizeof(binding), _hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DescriptorSetLayoutInfoVulkan::operator==(const DescriptorSetLayoutInfoVulkan& other) const
|
||||||
|
{
|
||||||
|
if (other._setLayouts.Count() != _setLayouts.Count())
|
||||||
|
return false;
|
||||||
|
if (other._typesUsageID != _typesUsageID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int32 index = 0; index < other._setLayouts.Count(); index++)
|
||||||
|
{
|
||||||
|
const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count();
|
||||||
|
if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount)
|
||||||
|
return false;
|
||||||
|
if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
DescriptorSetLayoutVulkan::DescriptorSetLayoutVulkan(GPUDeviceVulkan* device)
|
DescriptorSetLayoutVulkan::DescriptorSetLayoutVulkan(GPUDeviceVulkan* device)
|
||||||
: _device(device)
|
: _device(device)
|
||||||
{
|
{
|
||||||
@@ -370,20 +378,20 @@ PipelineLayoutVulkan::~PipelineLayoutVulkan()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, uint8* bindingToDynamicOffset)
|
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, VkBufferView* texelBufferView, uint8* bindingToDynamicOffset)
|
||||||
{
|
{
|
||||||
ASSERT(info.DescriptorTypesCount <= 64);
|
ASSERT(info.DescriptorTypesCount <= SpirvShaderDescriptorInfo::MaxDescriptors);
|
||||||
WriteDescriptors = writeDescriptors;
|
WriteDescriptors = writeDescriptors;
|
||||||
WritesCount = info.DescriptorTypesCount;
|
WritesCount = info.DescriptorTypesCount;
|
||||||
BindingToDynamicOffset = bindingToDynamicOffset;
|
BindingToDynamicOffset = bindingToDynamicOffset;
|
||||||
|
|
||||||
uint32 dynamicOffsetIndex = 0;
|
uint32 dynamicOffsetIndex = 0;
|
||||||
for (uint32 i = 0; i < info.DescriptorTypesCount; i++)
|
for (uint32 i = 0; i < info.DescriptorTypesCount; i++)
|
||||||
{
|
{
|
||||||
|
const auto& descriptor = info.DescriptorTypes[i];
|
||||||
writeDescriptors->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
writeDescriptors->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
writeDescriptors->dstBinding = i;
|
writeDescriptors->dstBinding = i;
|
||||||
writeDescriptors->descriptorCount = 1;
|
writeDescriptors->descriptorCount = descriptor.Count;
|
||||||
writeDescriptors->descriptorType = info.DescriptorTypes[i].DescriptorType;
|
writeDescriptors->descriptorType = descriptor.DescriptorType;
|
||||||
|
|
||||||
switch (writeDescriptors->descriptorType)
|
switch (writeDescriptors->descriptorType)
|
||||||
{
|
{
|
||||||
@@ -394,16 +402,20 @@ uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescrip
|
|||||||
break;
|
break;
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||||
writeDescriptors->pBufferInfo = bufferInfo++;
|
writeDescriptors->pBufferInfo = bufferInfo;
|
||||||
|
bufferInfo += descriptor.Count;
|
||||||
break;
|
break;
|
||||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||||
writeDescriptors->pImageInfo = imageInfo++;
|
writeDescriptors->pImageInfo = imageInfo;
|
||||||
|
imageInfo += descriptor.Count;
|
||||||
break;
|
break;
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||||
|
writeDescriptors->pTexelBufferView = texelBufferView;
|
||||||
|
texelBufferView += descriptor.Count;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CRASH;
|
CRASH;
|
||||||
@@ -412,7 +424,6 @@ uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescrip
|
|||||||
|
|
||||||
writeDescriptors++;
|
writeDescriptors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dynamicOffsetIndex;
|
return dynamicOffsetIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ protected:
|
|||||||
uint32 _typesUsageID = ~0;
|
uint32 _typesUsageID = ~0;
|
||||||
|
|
||||||
void CacheTypesUsageID();
|
void CacheTypesUsageID();
|
||||||
void AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -138,11 +137,6 @@ public:
|
|||||||
return _setLayouts;
|
return _setLayouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const uint32* GetLayoutTypes() const
|
|
||||||
{
|
|
||||||
return _layoutTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32 GetTypesUsageID() const
|
inline uint32 GetTypesUsageID() const
|
||||||
{
|
{
|
||||||
return _typesUsageID;
|
return _typesUsageID;
|
||||||
@@ -160,25 +154,7 @@ public:
|
|||||||
_setLayouts = info._setLayouts;
|
_setLayouts = info._setLayouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator ==(const DescriptorSetLayoutInfoVulkan& other) const
|
bool operator==(const DescriptorSetLayoutInfoVulkan& other) const;
|
||||||
{
|
|
||||||
if (other._setLayouts.Count() != _setLayouts.Count())
|
|
||||||
return false;
|
|
||||||
if (other._typesUsageID != _typesUsageID)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (int32 index = 0; index < other._setLayouts.Count(); index++)
|
|
||||||
{
|
|
||||||
const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count();
|
|
||||||
if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend inline uint32 GetHash(const DescriptorSetLayoutInfoVulkan& key)
|
friend inline uint32 GetHash(const DescriptorSetLayoutInfoVulkan& key)
|
||||||
{
|
{
|
||||||
@@ -413,6 +389,7 @@ struct DescriptorSetWriteContainerVulkan
|
|||||||
{
|
{
|
||||||
Array<VkDescriptorImageInfo> DescriptorImageInfo;
|
Array<VkDescriptorImageInfo> DescriptorImageInfo;
|
||||||
Array<VkDescriptorBufferInfo> DescriptorBufferInfo;
|
Array<VkDescriptorBufferInfo> DescriptorBufferInfo;
|
||||||
|
Array<VkBufferView> DescriptorTexelBufferView;
|
||||||
Array<VkWriteDescriptorSet> DescriptorWrites;
|
Array<VkWriteDescriptorSet> DescriptorWrites;
|
||||||
Array<byte> BindingToDynamicOffset;
|
Array<byte> BindingToDynamicOffset;
|
||||||
|
|
||||||
@@ -420,6 +397,7 @@ struct DescriptorSetWriteContainerVulkan
|
|||||||
{
|
{
|
||||||
DescriptorImageInfo.Resize(0);
|
DescriptorImageInfo.Resize(0);
|
||||||
DescriptorBufferInfo.Resize(0);
|
DescriptorBufferInfo.Resize(0);
|
||||||
|
DescriptorTexelBufferView.Resize(0);
|
||||||
DescriptorWrites.Resize(0);
|
DescriptorWrites.Resize(0);
|
||||||
BindingToDynamicOffset.Resize(0);
|
BindingToDynamicOffset.Resize(0);
|
||||||
}
|
}
|
||||||
@@ -436,26 +414,24 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
uint32 SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, byte* bindingToDynamicOffset);
|
uint32 SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, VkBufferView* texelBufferView, byte* bindingToDynamicOffset);
|
||||||
|
|
||||||
bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
|
bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
auto* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo + index);
|
||||||
ASSERT(bufferInfo);
|
|
||||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
||||||
return edited;
|
return edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteDynamicUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 dynamicOffset) const
|
bool WriteDynamicUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 dynamicOffset, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
|
||||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
auto* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo + index);
|
||||||
ASSERT(bufferInfo);
|
|
||||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
||||||
@@ -464,63 +440,61 @@ public:
|
|||||||
return edited;
|
return edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteSampler(uint32 descriptorIndex, VkSampler sampler) const
|
bool WriteSampler(uint32 descriptorIndex, VkSampler sampler, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
auto* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo + index);
|
||||||
ASSERT(imageInfo);
|
|
||||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->sampler, sampler);
|
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->sampler, sampler);
|
||||||
return edited;
|
return edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
|
bool WriteImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
auto* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo + index);
|
||||||
ASSERT(imageInfo);
|
|
||||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageView, imageView);
|
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageView, imageView);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageLayout, layout);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageLayout, layout);
|
||||||
return edited;
|
return edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteStorageImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
|
bool WriteStorageImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
|
||||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
auto* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo + index);
|
||||||
ASSERT(imageInfo);
|
|
||||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageView, imageView);
|
bool edited = DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageView, imageView);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageLayout, layout);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(imageInfo->imageLayout, layout);
|
||||||
return edited;
|
return edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteStorageTexelBuffer(uint32 descriptorIndex, const VkBufferView* bufferView) const
|
bool WriteStorageTexelBuffer(uint32 descriptorIndex, VkBufferView bufferView, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
|
||||||
WriteDescriptors[descriptorIndex].pTexelBufferView = bufferView;
|
auto* bufferInfo = const_cast<VkBufferView*>(WriteDescriptors[descriptorIndex].pTexelBufferView + index);
|
||||||
|
*bufferInfo = bufferView;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteStorageBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
|
bool WriteStorageBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
|
||||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
auto* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo + index);
|
||||||
ASSERT(bufferInfo);
|
|
||||||
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
bool edited = DescriptorSet::CopyAndReturnNotEqual(bufferInfo->buffer, buffer);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->offset, offset);
|
||||||
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
edited |= DescriptorSet::CopyAndReturnNotEqual(bufferInfo->range, range);
|
||||||
return edited;
|
return edited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteUniformTexelBuffer(uint32 descriptorIndex, const VkBufferView* view) const
|
bool WriteUniformTexelBuffer(uint32 descriptorIndex, VkBufferView view, uint32 index = 0) const
|
||||||
{
|
{
|
||||||
ASSERT(descriptorIndex < WritesCount);
|
ASSERT(descriptorIndex < WritesCount);
|
||||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
|
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
|
||||||
return DescriptorSet::CopyAndReturnNotEqual(WriteDescriptors[descriptorIndex].pTexelBufferView, view);
|
auto* bufferInfo = const_cast<VkBufferView*>(WriteDescriptors[descriptorIndex].pTexelBufferView + index);
|
||||||
|
return DescriptorSet::CopyAndReturnNotEqual(*bufferInfo, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDescriptorSet(VkDescriptorSet descriptorSet) const
|
void SetDescriptorSet(VkDescriptorSet descriptorSet) const
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ void GPUBufferViewVulkan::Release()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUBufferViewVulkan::DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
void GPUBufferViewVulkan::DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||||
{
|
{
|
||||||
ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
|
ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
|
||||||
bufferView = &View;
|
bufferView = View;
|
||||||
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +60,10 @@ void GPUBufferViewVulkan::DescriptorAsStorageBuffer(GPUContextVulkan* context, V
|
|||||||
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUBufferViewVulkan::DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
void GPUBufferViewVulkan::DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||||
{
|
{
|
||||||
ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
|
ASSERT_LOW_LAYER(View != VK_NULL_HANDLE);
|
||||||
bufferView = &View;
|
bufferView = View;
|
||||||
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
context->AddBufferBarrier(Owner, VK_ACCESS_SHADER_READ_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [DescriptorOwnerResourceVulkan]
|
// [DescriptorOwnerResourceVulkan]
|
||||||
void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView) override;
|
void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView) override;
|
||||||
void DescriptorAsStorageBuffer(GPUContextVulkan* context, VkBuffer& buffer, VkDeviceSize& offset, VkDeviceSize& range) override;
|
void DescriptorAsStorageBuffer(GPUContextVulkan* context, VkBuffer& buffer, VkDeviceSize& offset, VkDeviceSize& range) override;
|
||||||
void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView) override;
|
void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -444,21 +444,23 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
|||||||
const auto& descriptor = descriptorInfo.DescriptorTypes[i];
|
const auto& descriptor = descriptorInfo.DescriptorTypes[i];
|
||||||
const int32 descriptorIndex = descriptor.Binding;
|
const int32 descriptorIndex = descriptor.Binding;
|
||||||
DescriptorOwnerResourceVulkan** handles = _handles[(int32)descriptor.BindingType];
|
DescriptorOwnerResourceVulkan** handles = _handles[(int32)descriptor.BindingType];
|
||||||
|
for (uint32 index = 0; index < descriptor.Count; index++)
|
||||||
|
{
|
||||||
|
const int32 slot = descriptor.Slot + index;
|
||||||
switch (descriptor.DescriptorType)
|
switch (descriptor.DescriptorType)
|
||||||
{
|
{
|
||||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||||
{
|
{
|
||||||
// Sampler
|
// Sampler
|
||||||
const VkSampler sampler = _samplerHandles[descriptor.Slot];
|
const VkSampler sampler = _samplerHandles[slot];
|
||||||
ASSERT(sampler);
|
ASSERT(sampler);
|
||||||
needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler);
|
needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||||
{
|
{
|
||||||
// Shader Resource (Texture)
|
// Shader Resource (Texture)
|
||||||
auto handle = (GPUTextureViewVulkan*)handles[descriptor.Slot];
|
auto handle = (GPUTextureViewVulkan*)handles[slot];
|
||||||
if (!handle)
|
if (!handle)
|
||||||
{
|
{
|
||||||
const auto dummy = _device->HelperResources.GetDummyTexture(descriptor.ResourceType);
|
const auto dummy = _device->HelperResources.GetDummyTexture(descriptor.ResourceType);
|
||||||
@@ -482,38 +484,39 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
|||||||
VkImageLayout layout;
|
VkImageLayout layout;
|
||||||
handle->DescriptorAsImage(this, imageView, layout);
|
handle->DescriptorAsImage(this, imageView, layout);
|
||||||
ASSERT(imageView != VK_NULL_HANDLE);
|
ASSERT(imageView != VK_NULL_HANDLE);
|
||||||
needsWrite |= dsWriter.WriteImage(descriptorIndex, imageView, layout);
|
needsWrite |= dsWriter.WriteImage(descriptorIndex, imageView, layout, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||||
{
|
{
|
||||||
// Shader Resource (Buffer)
|
// Shader Resource (Buffer)
|
||||||
auto sr = handles[descriptor.Slot];
|
auto sr = handles[slot];
|
||||||
if (!sr)
|
if (!sr)
|
||||||
{
|
{
|
||||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||||
sr = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
sr = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||||
}
|
}
|
||||||
const VkBufferView* bufferView;
|
VkBufferView bufferView;
|
||||||
sr->DescriptorAsUniformTexelBuffer(this, bufferView);
|
sr->DescriptorAsUniformTexelBuffer(this, bufferView);
|
||||||
needsWrite |= dsWriter.WriteUniformTexelBuffer(descriptorIndex, bufferView);
|
ASSERT(bufferView != VK_NULL_HANDLE);
|
||||||
|
needsWrite |= dsWriter.WriteUniformTexelBuffer(descriptorIndex, bufferView, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||||
{
|
{
|
||||||
// Unordered Access (Texture)
|
// Unordered Access (Texture)
|
||||||
auto ua = handles[descriptor.Slot];
|
auto ua = handles[slot];
|
||||||
ASSERT(ua);
|
ASSERT(ua);
|
||||||
VkImageView imageView;
|
VkImageView imageView;
|
||||||
VkImageLayout layout;
|
VkImageLayout layout;
|
||||||
ua->DescriptorAsStorageImage(this, imageView, layout);
|
ua->DescriptorAsStorageImage(this, imageView, layout);
|
||||||
needsWrite |= dsWriter.WriteStorageImage(descriptorIndex, imageView, layout);
|
needsWrite |= dsWriter.WriteStorageImage(descriptorIndex, imageView, layout, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||||
{
|
{
|
||||||
// Unordered Access (Buffer)
|
// Unordered Access (Buffer)
|
||||||
auto ua = handles[descriptor.Slot];
|
auto ua = handles[slot];
|
||||||
if (!ua)
|
if (!ua)
|
||||||
{
|
{
|
||||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||||
@@ -522,33 +525,34 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
|||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceSize offset, range;
|
VkDeviceSize offset, range;
|
||||||
ua->DescriptorAsStorageBuffer(this, buffer, offset, range);
|
ua->DescriptorAsStorageBuffer(this, buffer, offset, range);
|
||||||
needsWrite |= dsWriter.WriteStorageBuffer(descriptorIndex, buffer, offset, range);
|
needsWrite |= dsWriter.WriteStorageBuffer(descriptorIndex, buffer, offset, range, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||||
{
|
{
|
||||||
// Unordered Access (Buffer)
|
// Unordered Access (Buffer)
|
||||||
auto ua = handles[descriptor.Slot];
|
auto ua = handles[slot];
|
||||||
if (!ua)
|
if (!ua)
|
||||||
{
|
{
|
||||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||||
ua = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
ua = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
|
||||||
}
|
}
|
||||||
const VkBufferView* bufferView;
|
VkBufferView bufferView;
|
||||||
ua->DescriptorAsStorageTexelBuffer(this, bufferView);
|
ua->DescriptorAsStorageTexelBuffer(this, bufferView);
|
||||||
needsWrite |= dsWriter.WriteStorageTexelBuffer(descriptorIndex, bufferView);
|
ASSERT(bufferView != VK_NULL_HANDLE);
|
||||||
|
needsWrite |= dsWriter.WriteStorageTexelBuffer(descriptorIndex, bufferView, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||||
{
|
{
|
||||||
// Constant Buffer
|
// Constant Buffer
|
||||||
auto cb = handles[descriptor.Slot];
|
auto cb = handles[slot];
|
||||||
ASSERT(cb);
|
ASSERT(cb);
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceSize offset, range;
|
VkDeviceSize offset, range;
|
||||||
uint32 dynamicOffset;
|
uint32 dynamicOffset;
|
||||||
cb->DescriptorAsDynamicUniformBuffer(this, buffer, offset, range, dynamicOffset);
|
cb->DescriptorAsDynamicUniformBuffer(this, buffer, offset, range, dynamicOffset);
|
||||||
needsWrite |= dsWriter.WriteDynamicUniformBuffer(descriptorIndex, buffer, offset, range, dynamicOffset);
|
needsWrite |= dsWriter.WriteDynamicUniformBuffer(descriptorIndex, buffer, offset, range, dynamicOffset, index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -558,6 +562,7 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState)
|
void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -782,7 +782,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The GPU context. Can be used to add memory barriers to the pipeline before binding the descriptor to the pipeline.</param>
|
/// <param name="context">The GPU context. Can be used to add memory barriers to the pipeline before binding the descriptor to the pipeline.</param>
|
||||||
/// <param name="bufferView">The buffer view.</param>
|
/// <param name="bufferView">The buffer view.</param>
|
||||||
virtual void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
virtual void DescriptorAsUniformTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||||
{
|
{
|
||||||
CRASH;
|
CRASH;
|
||||||
}
|
}
|
||||||
@@ -804,7 +804,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The GPU context. Can be used to add memory barriers to the pipeline before binding the descriptor to the pipeline.</param>
|
/// <param name="context">The GPU context. Can be used to add memory barriers to the pipeline before binding the descriptor to the pipeline.</param>
|
||||||
/// <param name="bufferView">The buffer view.</param>
|
/// <param name="bufferView">The buffer view.</param>
|
||||||
virtual void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, const VkBufferView*& bufferView)
|
virtual void DescriptorAsStorageTexelBuffer(GPUContextVulkan* context, VkBufferView& bufferView)
|
||||||
{
|
{
|
||||||
CRASH;
|
CRASH;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,11 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState()
|
|||||||
uint32 dynamicOffsetsCount = 0;
|
uint32 dynamicOffsetsCount = 0;
|
||||||
if (DescriptorInfo.DescriptorTypesCount != 0)
|
if (DescriptorInfo.DescriptorTypesCount != 0)
|
||||||
{
|
{
|
||||||
|
// TODO: merge into a single allocation
|
||||||
_pipelineState->DSWriteContainer.DescriptorWrites.AddZeroed(DescriptorInfo.DescriptorTypesCount);
|
_pipelineState->DSWriteContainer.DescriptorWrites.AddZeroed(DescriptorInfo.DescriptorTypesCount);
|
||||||
_pipelineState->DSWriteContainer.DescriptorImageInfo.AddZeroed(DescriptorInfo.ImageInfosCount);
|
_pipelineState->DSWriteContainer.DescriptorImageInfo.AddZeroed(DescriptorInfo.ImageInfosCount);
|
||||||
_pipelineState->DSWriteContainer.DescriptorBufferInfo.AddZeroed(DescriptorInfo.BufferInfosCount);
|
_pipelineState->DSWriteContainer.DescriptorBufferInfo.AddZeroed(DescriptorInfo.BufferInfosCount);
|
||||||
|
_pipelineState->DSWriteContainer.DescriptorTexelBufferView.AddZeroed(DescriptorInfo.TexelBufferViewsCount);
|
||||||
|
|
||||||
ASSERT(DescriptorInfo.DescriptorTypesCount < 255);
|
ASSERT(DescriptorInfo.DescriptorTypesCount < 255);
|
||||||
_pipelineState->DSWriteContainer.BindingToDynamicOffset.AddDefault(DescriptorInfo.DescriptorTypesCount);
|
_pipelineState->DSWriteContainer.BindingToDynamicOffset.AddDefault(DescriptorInfo.DescriptorTypesCount);
|
||||||
@@ -62,9 +64,10 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState()
|
|||||||
VkWriteDescriptorSet* currentDescriptorWrite = _pipelineState->DSWriteContainer.DescriptorWrites.Get();
|
VkWriteDescriptorSet* currentDescriptorWrite = _pipelineState->DSWriteContainer.DescriptorWrites.Get();
|
||||||
VkDescriptorImageInfo* currentImageInfo = _pipelineState->DSWriteContainer.DescriptorImageInfo.Get();
|
VkDescriptorImageInfo* currentImageInfo = _pipelineState->DSWriteContainer.DescriptorImageInfo.Get();
|
||||||
VkDescriptorBufferInfo* currentBufferInfo = _pipelineState->DSWriteContainer.DescriptorBufferInfo.Get();
|
VkDescriptorBufferInfo* currentBufferInfo = _pipelineState->DSWriteContainer.DescriptorBufferInfo.Get();
|
||||||
|
VkBufferView* currentTexelBufferView = _pipelineState->DSWriteContainer.DescriptorTexelBufferView.Get();
|
||||||
uint8* currentBindingToDynamicOffsetMap = _pipelineState->DSWriteContainer.BindingToDynamicOffset.Get();
|
uint8* currentBindingToDynamicOffsetMap = _pipelineState->DSWriteContainer.BindingToDynamicOffset.Get();
|
||||||
|
|
||||||
dynamicOffsetsCount = _pipelineState->DSWriter.SetupDescriptorWrites(DescriptorInfo, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentBindingToDynamicOffsetMap);
|
dynamicOffsetsCount = _pipelineState->DSWriter.SetupDescriptorWrites(DescriptorInfo, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentTexelBufferView, currentBindingToDynamicOffsetMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pipelineState->DynamicOffsets.AddZeroed(dynamicOffsetsCount);
|
_pipelineState->DynamicOffsets.AddZeroed(dynamicOffsetsCount);
|
||||||
@@ -337,9 +340,11 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
|||||||
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// TODO: merge into a single allocation for a whole PSO
|
||||||
DSWriteContainer.DescriptorWrites.AddZeroed(descriptor->DescriptorTypesCount);
|
DSWriteContainer.DescriptorWrites.AddZeroed(descriptor->DescriptorTypesCount);
|
||||||
DSWriteContainer.DescriptorImageInfo.AddZeroed(descriptor->ImageInfosCount);
|
DSWriteContainer.DescriptorImageInfo.AddZeroed(descriptor->ImageInfosCount);
|
||||||
DSWriteContainer.DescriptorBufferInfo.AddZeroed(descriptor->BufferInfosCount);
|
DSWriteContainer.DescriptorBufferInfo.AddZeroed(descriptor->BufferInfosCount);
|
||||||
|
DSWriteContainer.DescriptorTexelBufferView.AddZeroed(descriptor->TexelBufferViewsCount);
|
||||||
|
|
||||||
ASSERT(descriptor->DescriptorTypesCount < 255);
|
ASSERT(descriptor->DescriptorTypesCount < 255);
|
||||||
DSWriteContainer.BindingToDynamicOffset.AddDefault(descriptor->DescriptorTypesCount);
|
DSWriteContainer.BindingToDynamicOffset.AddDefault(descriptor->DescriptorTypesCount);
|
||||||
@@ -349,6 +354,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
|||||||
VkWriteDescriptorSet* currentDescriptorWrite = DSWriteContainer.DescriptorWrites.Get();
|
VkWriteDescriptorSet* currentDescriptorWrite = DSWriteContainer.DescriptorWrites.Get();
|
||||||
VkDescriptorImageInfo* currentImageInfo = DSWriteContainer.DescriptorImageInfo.Get();
|
VkDescriptorImageInfo* currentImageInfo = DSWriteContainer.DescriptorImageInfo.Get();
|
||||||
VkDescriptorBufferInfo* currentBufferInfo = DSWriteContainer.DescriptorBufferInfo.Get();
|
VkDescriptorBufferInfo* currentBufferInfo = DSWriteContainer.DescriptorBufferInfo.Get();
|
||||||
|
VkBufferView* currentTexelBufferView = DSWriteContainer.DescriptorTexelBufferView.Get();
|
||||||
byte* currentBindingToDynamicOffsetMap = DSWriteContainer.BindingToDynamicOffset.Get();
|
byte* currentBindingToDynamicOffsetMap = DSWriteContainer.BindingToDynamicOffset.Get();
|
||||||
uint32 dynamicOffsetsStart[DescriptorSet::GraphicsStagesCount];
|
uint32 dynamicOffsetsStart[DescriptorSet::GraphicsStagesCount];
|
||||||
uint32 dynamicOffsetsCount = 0;
|
uint32 dynamicOffsetsCount = 0;
|
||||||
@@ -360,12 +366,13 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
|||||||
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const uint32 numDynamicOffsets = DSWriter[stage].SetupDescriptorWrites(*descriptor, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentBindingToDynamicOffsetMap);
|
const uint32 numDynamicOffsets = DSWriter[stage].SetupDescriptorWrites(*descriptor, currentDescriptorWrite, currentImageInfo, currentBufferInfo, currentTexelBufferView, currentBindingToDynamicOffsetMap);
|
||||||
dynamicOffsetsCount += numDynamicOffsets;
|
dynamicOffsetsCount += numDynamicOffsets;
|
||||||
|
|
||||||
currentDescriptorWrite += descriptor->DescriptorTypesCount;
|
currentDescriptorWrite += descriptor->DescriptorTypesCount;
|
||||||
currentImageInfo += descriptor->ImageInfosCount;
|
currentImageInfo += descriptor->ImageInfosCount;
|
||||||
currentBufferInfo += descriptor->BufferInfosCount;
|
currentBufferInfo += descriptor->BufferInfosCount;
|
||||||
|
currentTexelBufferView += descriptor->TexelBufferViewsCount;
|
||||||
currentBindingToDynamicOffsetMap += descriptor->DescriptorTypesCount;
|
currentBindingToDynamicOffsetMap += descriptor->DescriptorTypesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,10 +94,16 @@ struct SpirvShaderDescriptorInfo
|
|||||||
/// The resource type.
|
/// The resource type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SpirvShaderResourceType ResourceType;
|
SpirvShaderResourceType ResourceType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of slots used by the descriptor (eg. array of textures size).
|
||||||
|
/// </summary>
|
||||||
|
uint32 Count;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16 ImageInfosCount;
|
uint16 ImageInfosCount;
|
||||||
uint16 BufferInfosCount;
|
uint16 BufferInfosCount;
|
||||||
|
uint32 TexelBufferViewsCount;
|
||||||
uint32 DescriptorTypesCount;
|
uint32 DescriptorTypesCount;
|
||||||
Descriptor DescriptorTypes[MaxDescriptors];
|
Descriptor DescriptorTypes[MaxDescriptors];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -151,7 +151,8 @@ namespace
|
|||||||
case D3D_SIT_TEXTURE:
|
case D3D_SIT_TEXTURE:
|
||||||
case D3D_SIT_STRUCTURED:
|
case D3D_SIT_STRUCTURED:
|
||||||
case D3D_SIT_BYTEADDRESS:
|
case D3D_SIT_BYTEADDRESS:
|
||||||
bindings.UsedSRsMask |= 1 << resDesc.BindPoint;
|
for (UINT shift = 0; shift < resDesc.BindCount; shift++)
|
||||||
|
bindings.UsedSRsMask |= 1 << (resDesc.BindPoint + shift);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Unordered Access
|
// Unordered Access
|
||||||
@@ -161,7 +162,8 @@ namespace
|
|||||||
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
||||||
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
||||||
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
||||||
bindings.UsedUAsMask |= 1 << resDesc.BindPoint;
|
for (UINT shift = 0; shift < resDesc.BindCount; shift++)
|
||||||
|
bindings.UsedUAsMask |= 1 << (resDesc.BindPoint + shift);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -381,13 +381,19 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
|
|||||||
|
|
||||||
// Shader Resource
|
// Shader Resource
|
||||||
case D3D_SIT_TEXTURE:
|
case D3D_SIT_TEXTURE:
|
||||||
bindings.UsedSRsMask |= 1 << resDesc.BindPoint;
|
for (UINT shift = 0; shift < resDesc.BindCount; shift++)
|
||||||
header.SrDimensions[resDesc.BindPoint] = resDesc.Dimension;
|
{
|
||||||
|
bindings.UsedSRsMask |= 1 << (resDesc.BindPoint + shift);
|
||||||
|
header.SrDimensions[resDesc.BindPoint + shift] = resDesc.Dimension;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case D3D_SIT_STRUCTURED:
|
case D3D_SIT_STRUCTURED:
|
||||||
case D3D_SIT_BYTEADDRESS:
|
case D3D_SIT_BYTEADDRESS:
|
||||||
bindings.UsedSRsMask |= 1 << resDesc.BindPoint;
|
for (UINT shift = 0; shift < resDesc.BindCount; shift++)
|
||||||
header.SrDimensions[resDesc.BindPoint] = D3D_SRV_DIMENSION_BUFFER;
|
{
|
||||||
|
bindings.UsedSRsMask |= 1 << (resDesc.BindPoint + shift);
|
||||||
|
header.SrDimensions[resDesc.BindPoint + shift] = D3D_SRV_DIMENSION_BUFFER;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Unordered Access
|
// Unordered Access
|
||||||
@@ -397,30 +403,10 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD
|
|||||||
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
||||||
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
||||||
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
||||||
bindings.UsedUAsMask |= 1 << resDesc.BindPoint;
|
for (UINT shift = 0; shift < resDesc.BindCount; shift++)
|
||||||
switch (resDesc.Dimension)
|
|
||||||
{
|
{
|
||||||
case D3D_SRV_DIMENSION_BUFFER:
|
bindings.UsedUAsMask |= 1 << (resDesc.BindPoint + shift);
|
||||||
header.UaDimensions[resDesc.BindPoint] = 1; // D3D12_UAV_DIMENSION_BUFFER;
|
header.SrDimensions[resDesc.BindPoint + shift] = (byte)resDesc.Dimension; // D3D_SRV_DIMENSION matches D3D12_UAV_DIMENSION
|
||||||
break;
|
|
||||||
case D3D_SRV_DIMENSION_TEXTURE1D:
|
|
||||||
header.UaDimensions[resDesc.BindPoint] = 2; // D3D12_UAV_DIMENSION_TEXTURE1D;
|
|
||||||
break;
|
|
||||||
case D3D_SRV_DIMENSION_TEXTURE1DARRAY:
|
|
||||||
header.UaDimensions[resDesc.BindPoint] = 3; // D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
|
|
||||||
break;
|
|
||||||
case D3D_SRV_DIMENSION_TEXTURE2D:
|
|
||||||
header.UaDimensions[resDesc.BindPoint] = 4; // D3D12_UAV_DIMENSION_TEXTURE2D;
|
|
||||||
break;
|
|
||||||
case D3D_SRV_DIMENSION_TEXTURE2DARRAY:
|
|
||||||
header.UaDimensions[resDesc.BindPoint] = 5; // D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
|
||||||
break;
|
|
||||||
case D3D_SRV_DIMENSION_TEXTURE3D:
|
|
||||||
header.UaDimensions[resDesc.BindPoint] = 8; // D3D12_UAV_DIMENSION_TEXTURE3D;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG(Error, "Unknown UAV resource {2} of type {0} at slot {1}", resDesc.Dimension, resDesc.BindPoint, String(resDesc.Name));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ namespace
|
|||||||
class Includer : public glslang::TShader::Includer
|
class Includer : public glslang::TShader::Includer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ShaderCompilationContext* _context;
|
ShaderCompilationContext* _context;
|
||||||
|
|
||||||
IncludeResult* include(const char* headerName, const char* includerName, int depth) const
|
IncludeResult* include(const char* headerName, const char* includerName, int depth) const
|
||||||
@@ -46,14 +45,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Includer(ShaderCompilationContext* context)
|
Includer(ShaderCompilationContext* context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [glslang::TShader::Include]
|
// [glslang::TShader::Include]
|
||||||
IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override
|
IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override
|
||||||
{
|
{
|
||||||
@@ -210,6 +207,7 @@ struct Descriptor
|
|||||||
int32 Slot;
|
int32 Slot;
|
||||||
int32 Binding;
|
int32 Binding;
|
||||||
int32 Size;
|
int32 Size;
|
||||||
|
int32 Count;
|
||||||
SpirvShaderResourceBindingType BindingType;
|
SpirvShaderResourceBindingType BindingType;
|
||||||
VkDescriptorType DescriptorType;
|
VkDescriptorType DescriptorType;
|
||||||
SpirvShaderResourceType ResourceType;
|
SpirvShaderResourceType ResourceType;
|
||||||
@@ -244,23 +242,13 @@ bool IsUavType(const glslang::TType& type)
|
|||||||
class DescriptorsCollector
|
class DescriptorsCollector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
int32 Images = 0;
|
||||||
int32 Images;
|
int32 Buffers = 0;
|
||||||
int32 Buffers;
|
int32 TexelBuffers = 0;
|
||||||
int32 DescriptorsCount;
|
int32 DescriptorsCount = 0;
|
||||||
Descriptor Descriptors[SpirvShaderDescriptorInfo::MaxDescriptors];
|
Descriptor Descriptors[SpirvShaderDescriptorInfo::MaxDescriptors];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
DescriptorsCollector()
|
|
||||||
{
|
|
||||||
Images = 0;
|
|
||||||
Buffers = 0;
|
|
||||||
DescriptorsCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Descriptor* Add(glslang::TVarEntryInfo& ent)
|
Descriptor* Add(glslang::TVarEntryInfo& ent)
|
||||||
{
|
{
|
||||||
const glslang::TType& type = ent.symbol->getType();
|
const glslang::TType& type = ent.symbol->getType();
|
||||||
@@ -374,28 +362,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the output info about shader uniforms usage
|
|
||||||
switch (descriptorType)
|
|
||||||
{
|
|
||||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
|
||||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
|
||||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
|
||||||
Images++;
|
|
||||||
break;
|
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
|
||||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
|
||||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
|
||||||
Buffers++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG(Warning, "Invalid descriptor type {0} for symbol {1}.", (int32)descriptorType, String(name));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto index = DescriptorsCount++;
|
const auto index = DescriptorsCount++;
|
||||||
auto& descriptor = Descriptors[index];
|
auto& descriptor = Descriptors[index];
|
||||||
descriptor.Binding = index;
|
descriptor.Binding = index;
|
||||||
@@ -405,6 +371,32 @@ public:
|
|||||||
descriptor.DescriptorType = descriptorType;
|
descriptor.DescriptorType = descriptorType;
|
||||||
descriptor.ResourceType = resourceType;
|
descriptor.ResourceType = resourceType;
|
||||||
descriptor.Name = name;
|
descriptor.Name = name;
|
||||||
|
descriptor.Count = type.isSizedArray() ? type.getCumulativeArraySize() : 1;
|
||||||
|
|
||||||
|
// Get the output info about shader uniforms usage
|
||||||
|
switch (descriptorType)
|
||||||
|
{
|
||||||
|
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||||
|
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||||
|
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||||
|
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||||
|
Images += descriptor.Count;
|
||||||
|
break;
|
||||||
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||||
|
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||||
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||||
|
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||||
|
Buffers += descriptor.Count;
|
||||||
|
break;
|
||||||
|
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||||
|
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||||
|
TexelBuffers += descriptor.Count;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(Warning, "Invalid descriptor type {0} for symbol {1}.", (int32)descriptorType, String(name));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return &descriptor;
|
return &descriptor;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -412,12 +404,10 @@ public:
|
|||||||
class MyIoMapResolver : public glslang::TDefaultIoResolverBase
|
class MyIoMapResolver : public glslang::TDefaultIoResolverBase
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int32 _set;
|
int32 _set;
|
||||||
DescriptorsCollector* _collector;
|
DescriptorsCollector* _collector;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
MyIoMapResolver(int32 set, DescriptorsCollector* collector, const glslang::TIntermediate& intermediate)
|
MyIoMapResolver(int32 set, DescriptorsCollector* collector, const glslang::TIntermediate& intermediate)
|
||||||
: TDefaultIoResolverBase(intermediate)
|
: TDefaultIoResolverBase(intermediate)
|
||||||
, _set(set)
|
, _set(set)
|
||||||
@@ -426,7 +416,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// [glslang::TDefaultIoResolverBase]
|
// [glslang::TDefaultIoResolverBase]
|
||||||
bool validateBinding(EShLanguage stage, glslang::TVarEntryInfo& ent) override
|
bool validateBinding(EShLanguage stage, glslang::TVarEntryInfo& ent) override
|
||||||
{
|
{
|
||||||
@@ -455,7 +444,9 @@ public:
|
|||||||
// Add resource
|
// Add resource
|
||||||
const auto descriptor = _collector->Add(ent);
|
const auto descriptor = _collector->Add(ent);
|
||||||
if (descriptor)
|
if (descriptor)
|
||||||
return ent.newBinding = reserveSlot(_set, descriptor->Binding);
|
{
|
||||||
|
return ent.newBinding = reserveSlot(_set, descriptor->Binding, descriptor->Count);
|
||||||
|
}
|
||||||
return ent.newBinding;
|
return ent.newBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +677,7 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
|||||||
// Process all descriptors
|
// Process all descriptors
|
||||||
header.DescriptorInfo.ImageInfosCount = descriptorsCollector.Images;
|
header.DescriptorInfo.ImageInfosCount = descriptorsCollector.Images;
|
||||||
header.DescriptorInfo.BufferInfosCount = descriptorsCollector.Buffers;
|
header.DescriptorInfo.BufferInfosCount = descriptorsCollector.Buffers;
|
||||||
|
header.DescriptorInfo.TexelBufferViewsCount = descriptorsCollector.TexelBuffers;
|
||||||
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
|
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
|
||||||
{
|
{
|
||||||
auto& descriptor = descriptorsCollector.Descriptors[i];
|
auto& descriptor = descriptorsCollector.Descriptors[i];
|
||||||
@@ -697,6 +689,7 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
|||||||
d.BindingType = descriptor.BindingType;
|
d.BindingType = descriptor.BindingType;
|
||||||
d.DescriptorType = descriptor.DescriptorType;
|
d.DescriptorType = descriptor.DescriptorType;
|
||||||
d.ResourceType = descriptor.ResourceType;
|
d.ResourceType = descriptor.ResourceType;
|
||||||
|
d.Count = descriptor.Count;
|
||||||
|
|
||||||
switch (descriptor.BindingType)
|
switch (descriptor.BindingType)
|
||||||
{
|
{
|
||||||
@@ -732,6 +725,7 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat
|
|||||||
// Mark as used and cache some data
|
// Mark as used and cache some data
|
||||||
cc.IsUsed = true;
|
cc.IsUsed = true;
|
||||||
cc.Size = descriptor.Size;
|
cc.Size = descriptor.Size;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user