本文整理汇总了C++中VkResourceRecord::AddParent方法的典型用法代码示例。如果您正苦于以下问题:C++ VkResourceRecord::AddParent方法的具体用法?C++ VkResourceRecord::AddParent怎么用?C++ VkResourceRecord::AddParent使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类VkResourceRecord
的用法示例。
在下文中一共展示了VkResourceRecord::AddParent方法的11个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。
示例1: vkBindBufferMemory
VkResult WrappedVulkan::vkBindBufferMemory(
VkDevice device,
VkBuffer buffer,
VkDeviceMemory mem,
VkDeviceSize memOffset)
{
VkResourceRecord *record = GetRecord(buffer);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BIND_BUFFER_MEM);
Serialise_vkBindBufferMemory(localSerialiser, device, buffer, mem, memOffset);
chunk = scope.Get();
}
// memory object bindings are immutable and must happen before creation or use,
// so this can always go into the record, even if a resource is created and bound
// to memory mid-frame
record->AddChunk(chunk);
record->AddParent(GetRecord(mem));
record->baseResource = GetResID(mem);
}
return ObjDisp(device)->BindBufferMemory(Unwrap(device), Unwrap(buffer), Unwrap(mem), memOffset);
}
示例2: vkCreatePipelineLayout
VkResult WrappedVulkan::vkCreatePipelineLayout(VkDevice device,
const VkPipelineLayoutCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkPipelineLayout *pPipelineLayout)
{
VkDescriptorSetLayout *unwrapped = GetTempArray<VkDescriptorSetLayout>(pCreateInfo->setLayoutCount);
for(uint32_t i = 0; i < pCreateInfo->setLayoutCount; i++)
unwrapped[i] = Unwrap(pCreateInfo->pSetLayouts[i]);
VkPipelineLayoutCreateInfo unwrappedInfo = *pCreateInfo;
unwrappedInfo.pSetLayouts = unwrapped;
VkResult ret = ObjDisp(device)->CreatePipelineLayout(Unwrap(device), &unwrappedInfo, pAllocator,
pPipelineLayout);
if(ret == VK_SUCCESS)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pPipelineLayout);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(CREATE_PIPE_LAYOUT);
Serialise_vkCreatePipelineLayout(localSerialiser, device, pCreateInfo, NULL, pPipelineLayout);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pPipelineLayout);
record->AddChunk(chunk);
for(uint32_t i = 0; i < pCreateInfo->setLayoutCount; i++)
{
VkResourceRecord *layoutrecord = GetRecord(pCreateInfo->pSetLayouts[i]);
record->AddParent(layoutrecord);
}
}
else
{
GetResourceManager()->AddLiveResource(id, *pPipelineLayout);
m_CreationInfo.m_PipelineLayout[id].Init(GetResourceManager(), m_CreationInfo, &unwrappedInfo);
}
}
return ret;
}
示例3: vkCreateImageView
VkResult WrappedVulkan::vkCreateImageView(
VkDevice device,
const VkImageViewCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkImageView* pView)
{
VkImageViewCreateInfo unwrappedInfo = *pCreateInfo;
unwrappedInfo.image = Unwrap(unwrappedInfo.image);
VkResult ret = ObjDisp(device)->CreateImageView(Unwrap(device), &unwrappedInfo, pAllocator, pView);
if(ret == VK_SUCCESS)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pView);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(CREATE_IMAGE_VIEW);
Serialise_vkCreateImageView(localSerialiser, device, pCreateInfo, NULL, pView);
chunk = scope.Get();
}
VkResourceRecord *imageRecord = GetRecord(pCreateInfo->image);
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pView);
record->AddChunk(chunk);
record->AddParent(imageRecord);
// store the base resource. Note images have a baseResource pointing
// to their memory, which we will also need so we store that separately
record->baseResource = imageRecord->GetResourceID();
record->baseResourceMem = imageRecord->baseResource;
record->sparseInfo = imageRecord->sparseInfo;
}
else
{
GetResourceManager()->AddLiveResource(id, *pView);
m_CreationInfo.m_ImageView[id].Init(GetResourceManager(), m_CreationInfo, &unwrappedInfo);
}
}
return ret;
}
示例4: vkCreateBufferView
VkResult WrappedVulkan::vkCreateBufferView(
VkDevice device,
const VkBufferViewCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkBufferView* pView)
{
VkBufferViewCreateInfo unwrappedInfo = *pCreateInfo;
unwrappedInfo.buffer = Unwrap(unwrappedInfo.buffer);
VkResult ret = ObjDisp(device)->CreateBufferView(Unwrap(device), &unwrappedInfo, pAllocator, pView);
if(ret == VK_SUCCESS)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pView);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(CREATE_BUFFER_VIEW);
Serialise_vkCreateBufferView(localSerialiser, device, pCreateInfo, NULL, pView);
chunk = scope.Get();
}
VkResourceRecord *bufferRecord = GetRecord(pCreateInfo->buffer);
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pView);
record->AddChunk(chunk);
record->AddParent(bufferRecord);
// store the base resource
record->baseResource = bufferRecord->baseResource;
record->sparseInfo = bufferRecord->sparseInfo;
}
else
{
GetResourceManager()->AddLiveResource(id, *pView);
m_CreationInfo.m_BufferView[id].Init(GetResourceManager(), m_CreationInfo, &unwrappedInfo);
}
}
return ret;
}
示例5: vkBindImageMemory
VkResult WrappedVulkan::vkBindImageMemory(
VkDevice device,
VkImage image,
VkDeviceMemory mem,
VkDeviceSize memOffset)
{
VkResourceRecord *record = GetRecord(image);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BIND_IMAGE_MEM);
Serialise_vkBindImageMemory(localSerialiser, device, image, mem, memOffset);
chunk = scope.Get();
}
// memory object bindings are immutable and must happen before creation or use,
// so this can always go into the record, even if a resource is created and bound
// to memory mid-frame
record->AddChunk(chunk);
record->AddParent(GetRecord(mem));
// images are a base resource but we want to track where their memory comes from.
// Anything that looks up a baseResource for an image knows not to chase further
// than the image.
record->baseResource = GetResID(mem);
}
return ObjDisp(device)->BindImageMemory(Unwrap(device), Unwrap(image), Unwrap(mem), memOffset);
}
示例6: vkEnumeratePhysicalDevices
VkResult WrappedVulkan::vkEnumeratePhysicalDevices(
VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices)
{
uint32_t count;
VkResult vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, NULL);
if(vkr != VK_SUCCESS)
return vkr;
VkPhysicalDevice *devices = new VkPhysicalDevice[count];
vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, devices);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_PhysicalDevices.resize(count);
for(uint32_t i=0; i < count; i++)
{
// it's perfectly valid for enumerate type functions to return the same handle
// each time. If that happens, we will already have a wrapper created so just
// return the wrapped object to the user and do nothing else
if(m_PhysicalDevices[i] != VK_NULL_HANDLE)
{
GetWrapped(m_PhysicalDevices[i])->RewrapObject(devices[i]);
devices[i] = m_PhysicalDevices[i];
}
else
{
GetResourceManager()->WrapResource(instance, devices[i]);
if(m_State >= WRITING)
{
// add the record first since it's used in the serialise function below to fetch
// the memory indices
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(devices[i]);
RDCASSERT(record);
record->memProps = new VkPhysicalDeviceMemoryProperties();
ObjDisp(devices[i])->GetPhysicalDeviceMemoryProperties(Unwrap(devices[i]), record->memProps);
m_PhysicalDevices[i] = devices[i];
// we remap memory indices to discourage coherent maps as much as possible
RemapMemoryIndices(record->memProps, &record->memIdxMap);
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(ENUM_PHYSICALS);
Serialise_vkEnumeratePhysicalDevices(localSerialiser, instance, &i, &devices[i]);
record->AddChunk(scope.Get());
}
VkResourceRecord *instrecord = GetRecord(instance);
instrecord->AddParent(record);
// treat physical devices as pool members of the instance (ie. freed when the instance dies)
{
instrecord->LockChunks();
instrecord->pooledChildren.push_back(record);
instrecord->UnlockChunks();
}
}
}
}
if(pPhysicalDeviceCount) *pPhysicalDeviceCount = count;
if(pPhysicalDevices) memcpy(pPhysicalDevices, devices, count*sizeof(VkPhysicalDevice));
SAFE_DELETE_ARRAY(devices);
return VK_SUCCESS;
}
示例7: vkAllocateDescriptorSets
VkResult WrappedVulkan::vkAllocateDescriptorSets(
VkDevice device,
const VkDescriptorSetAllocateInfo* pAllocateInfo,
VkDescriptorSet* pDescriptorSets)
{
size_t tempmemSize = sizeof(VkDescriptorSetAllocateInfo) + sizeof(VkDescriptorSetLayout)*pAllocateInfo->descriptorSetCount;
byte *memory = GetTempMemory(tempmemSize);
VkDescriptorSetAllocateInfo *unwrapped = (VkDescriptorSetAllocateInfo *)memory;
VkDescriptorSetLayout *layouts = (VkDescriptorSetLayout *)(unwrapped + 1);
*unwrapped = *pAllocateInfo;
unwrapped->pSetLayouts = layouts;
unwrapped->descriptorPool = Unwrap(unwrapped->descriptorPool);
for(uint32_t i=0; i < pAllocateInfo->descriptorSetCount; i++)
layouts[i] = Unwrap(pAllocateInfo->pSetLayouts[i]);
VkResult ret = ObjDisp(device)->AllocateDescriptorSets(Unwrap(device), unwrapped, pDescriptorSets);
if(ret != VK_SUCCESS) return ret;
for(uint32_t i=0; i < pAllocateInfo->descriptorSetCount; i++)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pDescriptorSets[i]);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
VkDescriptorSetAllocateInfo info = *pAllocateInfo;
info.descriptorSetCount = 1;
info.pSetLayouts += i;
SCOPED_SERIALISE_CONTEXT(ALLOC_DESC_SET);
Serialise_vkAllocateDescriptorSets(localSerialiser, device, &info, &pDescriptorSets[i]);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pDescriptorSets[i]);
record->AddChunk(chunk);
ResourceId layoutID = GetResID(pAllocateInfo->pSetLayouts[i]);
VkResourceRecord *layoutRecord = GetRecord(pAllocateInfo->pSetLayouts[i]);
VkResourceRecord *poolrecord = GetRecord(pAllocateInfo->descriptorPool);
{
poolrecord->LockChunks();
poolrecord->pooledChildren.push_back(record);
poolrecord->UnlockChunks();
}
record->pool = poolrecord;
record->AddParent(poolrecord);
record->AddParent(GetResourceManager()->GetResourceRecord(layoutID));
// just always treat descriptor sets as dirty
{
SCOPED_LOCK(m_CapTransitionLock);
if(m_State != WRITING_CAPFRAME)
GetResourceManager()->MarkDirtyResource(id);
else
GetResourceManager()->MarkPendingDirty(id);
}
record->descInfo = new DescriptorSetData();
record->descInfo->layout = layoutRecord->descInfo->layout;
record->descInfo->layout->CreateBindingsArray(record->descInfo->descBindings);
}
else
{
GetResourceManager()->AddLiveResource(id, pDescriptorSets[i]);
}
}
return ret;
}
示例8: vkGetSwapchainImagesKHR
VkResult WrappedVulkan::vkGetSwapchainImagesKHR(
VkDevice device,
VkSwapchainKHR swapchain,
uint32_t* pCount,
VkImage* pSwapchainImages)
{
// make sure we always get the size
uint32_t dummySize = 0;
if(pCount == NULL)
pCount = &dummySize;
VkResult ret = ObjDisp(device)->GetSwapchainImagesKHR(Unwrap(device), Unwrap(swapchain), pCount, pSwapchainImages);
if(pSwapchainImages && m_State >= WRITING)
{
uint32_t numImages = *pCount;
VkResourceRecord *swapRecord = GetRecord(swapchain);
for(uint32_t i=0; i < numImages; i++)
{
// these were all wrapped and serialised on swapchain create - we just have to
// return the wrapped image in that case
if(swapRecord->swapInfo->images[i].im != VK_NULL_HANDLE)
{
pSwapchainImages[i] = swapRecord->swapInfo->images[i].im;
}
else
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pSwapchainImages[i]);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(GET_SWAPCHAIN_IMAGE);
Serialise_vkGetSwapchainImagesKHR(localSerialiser, device, swapchain, &i, &pSwapchainImages[i]);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pSwapchainImages[i]);
VkResourceRecord *swaprecord = GetRecord(swapchain);
record->SpecialResource = true;
record->AddParent(swaprecord);
// note we add the chunk to the swap record, that way when the swapchain is created it will
// always create all of its images on replay. The image's record is kept around for reference
// tracking and any other chunks. Because it has a parent relationship on the swapchain, if
// the image is referenced the swapchain (and thus all the getimages) will be included.
swaprecord->AddChunk(chunk);
}
else
{
GetResourceManager()->AddLiveResource(id, pSwapchainImages[i]);
}
}
}
}
return ret;
}
示例9: vkCreateFramebuffer
VkResult WrappedVulkan::vkCreateFramebuffer(
VkDevice device,
const VkFramebufferCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkFramebuffer* pFramebuffer)
{
VkImageView *unwrapped = GetTempArray<VkImageView>(pCreateInfo->attachmentCount);
for(uint32_t i=0; i < pCreateInfo->attachmentCount; i++)
unwrapped[i] = Unwrap(pCreateInfo->pAttachments[i]);
VkFramebufferCreateInfo unwrappedInfo = *pCreateInfo;
unwrappedInfo.renderPass = Unwrap(unwrappedInfo.renderPass);
unwrappedInfo.pAttachments = unwrapped;
VkResult ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), &unwrappedInfo, pAllocator, pFramebuffer);
if(ret == VK_SUCCESS)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pFramebuffer);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(CREATE_FRAMEBUFFER);
Serialise_vkCreateFramebuffer(localSerialiser, device, pCreateInfo, NULL, pFramebuffer);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pFramebuffer);
record->AddChunk(chunk);
record->imageAttachments = new VkResourceRecord*[VkResourceRecord::MaxImageAttachments];
RDCASSERT(pCreateInfo->attachmentCount <= VkResourceRecord::MaxImageAttachments);
RDCEraseMem(record->imageAttachments, sizeof(ResourceId)*VkResourceRecord::MaxImageAttachments);
if(pCreateInfo->renderPass != VK_NULL_HANDLE)
record->AddParent(GetRecord(pCreateInfo->renderPass));
for(uint32_t i=0; i < pCreateInfo->attachmentCount; i++)
{
VkResourceRecord *attRecord = GetRecord(pCreateInfo->pAttachments[i]);
record->AddParent(attRecord);
record->imageAttachments[i] = attRecord;
}
}
else
{
GetResourceManager()->AddLiveResource(id, *pFramebuffer);
m_CreationInfo.m_Framebuffer[id].Init(GetResourceManager(), m_CreationInfo, &unwrappedInfo);
}
}
return ret;
}
示例10: vkCreateComputePipelines
VkResult WrappedVulkan::vkCreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache,
uint32_t count,
const VkComputePipelineCreateInfo *pCreateInfos,
const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipelines)
{
VkComputePipelineCreateInfo *unwrapped = GetTempArray<VkComputePipelineCreateInfo>(count);
for(uint32_t i = 0; i < count; i++)
{
unwrapped[i] = pCreateInfos[i];
unwrapped[i].stage.module = Unwrap(unwrapped[i].stage.module);
unwrapped[i].layout = Unwrap(unwrapped[i].layout);
unwrapped[i].basePipelineHandle = Unwrap(unwrapped[i].basePipelineHandle);
}
VkResult ret = ObjDisp(device)->CreateComputePipelines(Unwrap(device), Unwrap(pipelineCache),
count, unwrapped, pAllocator, pPipelines);
if(ret == VK_SUCCESS)
{
for(uint32_t i = 0; i < count; i++)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pPipelines[i]);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(CREATE_COMPUTE_PIPE);
Serialise_vkCreateComputePipelines(localSerialiser, device, pipelineCache, 1,
&pCreateInfos[i], NULL, &pPipelines[i]);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pPipelines[i]);
record->AddChunk(chunk);
if(pipelineCache != VK_NULL_HANDLE)
{
VkResourceRecord *cacherecord = GetRecord(pipelineCache);
record->AddParent(cacherecord);
}
VkResourceRecord *layoutrecord = GetRecord(pCreateInfos[i].layout);
record->AddParent(layoutrecord);
VkResourceRecord *modulerecord = GetRecord(pCreateInfos[i].stage.module);
record->AddParent(modulerecord);
}
else
{
GetResourceManager()->AddLiveResource(id, pPipelines[i]);
m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, &unwrapped[i]);
}
}
}
return ret;
}
示例11: vkCreateGraphicsPipelines
VkResult WrappedVulkan::vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache,
uint32_t count,
const VkGraphicsPipelineCreateInfo *pCreateInfos,
const VkAllocationCallbacks *pAllocator,
VkPipeline *pPipelines)
{
// conservatively request memory for 5 stages on each pipeline
// (worst case - can't have compute stage). Avoids needing to count
byte *unwrapped = GetTempMemory(sizeof(VkGraphicsPipelineCreateInfo) * count +
sizeof(VkPipelineShaderStageCreateInfo) * count * 5);
// keep pipelines first in the memory, then the stages
VkGraphicsPipelineCreateInfo *unwrappedInfos = (VkGraphicsPipelineCreateInfo *)unwrapped;
VkPipelineShaderStageCreateInfo *nextUnwrappedStages =
(VkPipelineShaderStageCreateInfo *)(unwrappedInfos + count);
for(uint32_t i = 0; i < count; i++)
{
VkPipelineShaderStageCreateInfo *unwrappedStages = nextUnwrappedStages;
nextUnwrappedStages += pCreateInfos[i].stageCount;
for(uint32_t j = 0; j < pCreateInfos[i].stageCount; j++)
{
unwrappedStages[j] = pCreateInfos[i].pStages[j];
unwrappedStages[j].module = Unwrap(unwrappedStages[j].module);
}
unwrappedInfos[i] = pCreateInfos[i];
unwrappedInfos[i].pStages = unwrappedStages;
unwrappedInfos[i].layout = Unwrap(unwrappedInfos[i].layout);
unwrappedInfos[i].renderPass = Unwrap(unwrappedInfos[i].renderPass);
unwrappedInfos[i].basePipelineHandle = Unwrap(unwrappedInfos[i].basePipelineHandle);
}
VkResult ret = ObjDisp(device)->CreateGraphicsPipelines(
Unwrap(device), Unwrap(pipelineCache), count, unwrappedInfos, pAllocator, pPipelines);
if(ret == VK_SUCCESS)
{
for(uint32_t i = 0; i < count; i++)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pPipelines[i]);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
VkGraphicsPipelineCreateInfo modifiedCreateInfo;
const VkGraphicsPipelineCreateInfo *createInfo = &pCreateInfos[i];
// since we serialise one by one, we need to fixup basePipelineIndex
if(createInfo->basePipelineIndex != -1 && createInfo->basePipelineIndex < (int)i)
{
modifiedCreateInfo = *createInfo;
modifiedCreateInfo.basePipelineHandle = pPipelines[modifiedCreateInfo.basePipelineIndex];
modifiedCreateInfo.basePipelineIndex = -1;
createInfo = &modifiedCreateInfo;
}
SCOPED_SERIALISE_CONTEXT(CREATE_GRAPHICS_PIPE);
Serialise_vkCreateGraphicsPipelines(localSerialiser, device, pipelineCache, 1, createInfo,
NULL, &pPipelines[i]);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pPipelines[i]);
record->AddChunk(chunk);
if(pipelineCache != VK_NULL_HANDLE)
{
VkResourceRecord *cacherecord = GetRecord(pipelineCache);
record->AddParent(cacherecord);
}
VkResourceRecord *rprecord = GetRecord(pCreateInfos[i].renderPass);
record->AddParent(rprecord);
VkResourceRecord *layoutrecord = GetRecord(pCreateInfos[i].layout);
record->AddParent(layoutrecord);
for(uint32_t s = 0; s < pCreateInfos[i].stageCount; s++)
{
VkResourceRecord *modulerecord = GetRecord(pCreateInfos[i].pStages[s].module);
record->AddParent(modulerecord);
}
}
else
{
GetResourceManager()->AddLiveResource(id, pPipelines[i]);
m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, &unwrappedInfos[i]);
}
}
}
return ret;
}