|
|
|
@ -276,9 +276,9 @@ VkResult create_ui_pipeline(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult create_container(
|
|
|
|
|
UIContainerInput* container,
|
|
|
|
|
ContainerInput* container,
|
|
|
|
|
RenderContext* gpu,
|
|
|
|
|
UIContextStorage* context) {
|
|
|
|
|
UIContext* context) {
|
|
|
|
|
uint32_t index = 0xFFFFFFFF;
|
|
|
|
|
for(uint32_t i = 0; i < context->max_containers; i++) {
|
|
|
|
|
if(context->containers[i].id == 0x00000000) {
|
|
|
|
@ -292,29 +292,29 @@ VkResult create_container(
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(UIContainer), &context->containers[index].container, &context->containers[index].container_memory));
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUContainer), &context->containers[index].container, &context->containers[index].container_memory));
|
|
|
|
|
|
|
|
|
|
VkBuffer transfer;
|
|
|
|
|
VmaAllocation transfer_memory;
|
|
|
|
|
void* mapped;
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(UIContainer), &transfer, &transfer_memory, &mapped));
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(GPUContainer), &transfer, &transfer_memory, &mapped));
|
|
|
|
|
|
|
|
|
|
context->containers[index].data.offset[0] = container->offset[0];
|
|
|
|
|
context->containers[index].data.offset[1] = container->offset[1];
|
|
|
|
|
context->containers[index].data.size[0] = container->size[0];
|
|
|
|
|
context->containers[index].data.size[1] = container->size[1];
|
|
|
|
|
context->containers[index].data.anchor = container->anchor;
|
|
|
|
|
memcpy(mapped, &context->containers[index].data, sizeof(UIContainer));
|
|
|
|
|
memcpy(mapped, &context->containers[index].data, sizeof(GPUContainer));
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->containers[index].container, 0, 0, sizeof(UIContainer));
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->containers[index].container, 0, 0, sizeof(GPUContainer));
|
|
|
|
|
VK_RESULT(command_end_single(gpu->device, command_buffer, gpu->transfer_pool, gpu->transfer_queue));
|
|
|
|
|
destroy_transfer_buffer(gpu->allocator, transfer, transfer_memory);
|
|
|
|
|
|
|
|
|
|
context->containers[index].address = buffer_address(gpu->device, context->containers[index].container);
|
|
|
|
|
context->containers[index].id = container->id;
|
|
|
|
|
context->containers[index].layer_count = container->layer_count;
|
|
|
|
|
context->containers[index].layers = malloc(sizeof(UILayerStorage)*container->layer_count);
|
|
|
|
|
context->containers[index].layers = malloc(sizeof(Layer)*container->layer_count);
|
|
|
|
|
for(uint32_t i = 0; i < container->layer_count; i++) {
|
|
|
|
|
VK_RESULT(create_layer(i, &container->layers[i], gpu, &context->containers[index]));
|
|
|
|
|
}
|
|
|
|
@ -324,19 +324,19 @@ VkResult create_container(
|
|
|
|
|
|
|
|
|
|
VkResult create_layer(
|
|
|
|
|
uint32_t index,
|
|
|
|
|
UILayerInput* input,
|
|
|
|
|
LayerInput* input,
|
|
|
|
|
RenderContext* gpu,
|
|
|
|
|
UIContainerStorage* container) {
|
|
|
|
|
Container* container) {
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sizeof(UILayer), &container->layers[index].layer, &container->layers[index].layer_memory));
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sizeof(GPULayer), &container->layers[index].layer, &container->layers[index].layer_memory));
|
|
|
|
|
if(input->num_strings > 0) {
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(UIString)*input->num_strings, &container->layers[index].strings, &container->layers[index].strings_memory));
|
|
|
|
|
container->layers[index].strings_buffer = malloc(sizeof(UIString)*input->num_strings);
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUString)*input->num_strings, &container->layers[index].strings, &container->layers[index].strings_memory));
|
|
|
|
|
container->layers[index].strings_buffer = malloc(sizeof(GPUString)*input->num_strings);
|
|
|
|
|
}
|
|
|
|
|
if(input->num_codes + input->num_drawables > 0) {
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(UIDrawable)*(input->num_drawables + input->num_codes), &container->layers[index].drawables, &container->layers[index].drawables_memory));
|
|
|
|
|
container->layers[index].drawables_buffer = malloc(sizeof(UIDrawable)*input->num_drawables);
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUDrawable)*(input->num_drawables + input->num_codes), &container->layers[index].drawables, &container->layers[index].drawables_memory));
|
|
|
|
|
container->layers[index].drawables_buffer = malloc(sizeof(GPUDrawable)*input->num_drawables);
|
|
|
|
|
}
|
|
|
|
|
if(input->num_codes > 0) {
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(uint32_t)*input->num_codes, &container->layers[index].codes, &container->layers[index].codes_memory));
|
|
|
|
@ -346,7 +346,7 @@ VkResult create_layer(
|
|
|
|
|
VkBuffer transfer;
|
|
|
|
|
VmaAllocation transfer_memory;
|
|
|
|
|
void* mapped;
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(UILayer) + sizeof(UIString) * input->num_strings + sizeof(UIDrawable)*input->num_drawables + sizeof(uint32_t)*input->num_codes, &transfer, &transfer_memory, &mapped));
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(GPULayer) + sizeof(GPUString) * input->num_strings + sizeof(GPUDrawable)*input->num_drawables + sizeof(uint32_t)*input->num_codes, &transfer, &transfer_memory, &mapped));
|
|
|
|
|
|
|
|
|
|
if(input->num_strings > 0) {
|
|
|
|
|
container->layers[index].data.strings = buffer_address(gpu->device, container->layers[index].strings);
|
|
|
|
@ -380,35 +380,35 @@ VkResult create_layer(
|
|
|
|
|
container->layers[index].data.max_strings = input->num_strings;
|
|
|
|
|
container->layers[index].data.num_drawables = input->num_drawables;
|
|
|
|
|
container->layers[index].data.container = container->address;
|
|
|
|
|
memcpy(mapped, &container->layers[index].data, sizeof(UILayer));
|
|
|
|
|
memcpy(mapped, &container->layers[index].data, sizeof(GPULayer));
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].layer, 0, 0, sizeof(UILayer));
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].layer, 0, 0, sizeof(GPULayer));
|
|
|
|
|
if(input->num_strings > 0) {
|
|
|
|
|
UIString* strings = (UIString*)(mapped + sizeof(UILayer));
|
|
|
|
|
GPUString* strings = (GPUString*)(mapped + sizeof(GPULayer));
|
|
|
|
|
for(uint32_t i = 0; i < input->num_strings; i++) {
|
|
|
|
|
memcpy(&strings[i], &input->strings[i], sizeof(UIString));
|
|
|
|
|
memcpy(&container->layers[index].strings_buffer[i], &input->strings[i], sizeof(UIString));
|
|
|
|
|
memcpy(&strings[i], &input->strings[i], sizeof(GPUString));
|
|
|
|
|
memcpy(&container->layers[index].strings_buffer[i], &input->strings[i], sizeof(GPUString));
|
|
|
|
|
}
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].strings, sizeof(UILayer), 0, sizeof(UIString)*input->num_strings);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].strings, sizeof(GPULayer), 0, sizeof(GPUString)*input->num_strings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(input->num_drawables > 0) {
|
|
|
|
|
UIDrawable* drawables = (UIDrawable*)(mapped + sizeof(UILayer) + sizeof(UIString)*input->num_strings);
|
|
|
|
|
GPUDrawable* drawables = (GPUDrawable*)(mapped + sizeof(GPULayer) + sizeof(GPUString)*input->num_strings);
|
|
|
|
|
for(uint32_t i = 0; i < input->num_drawables; i++) {
|
|
|
|
|
memcpy(&drawables[i], &input->drawables[i], sizeof(UIDrawable));
|
|
|
|
|
memcpy(&container->layers[index].drawables_buffer[i], &input->drawables[i], sizeof(UIDrawable));
|
|
|
|
|
memcpy(&drawables[i], &input->drawables[i], sizeof(GPUDrawable));
|
|
|
|
|
memcpy(&container->layers[index].drawables_buffer[i], &input->drawables[i], sizeof(GPUDrawable));
|
|
|
|
|
}
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].drawables, sizeof(UILayer) + sizeof(UIString)*input->num_strings, 0, sizeof(UIDrawable)*input->num_drawables);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].drawables, sizeof(GPULayer) + sizeof(GPUString)*input->num_strings, 0, sizeof(GPUDrawable)*input->num_drawables);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(input->num_codes > 0) {
|
|
|
|
|
uint32_t* codes = (uint32_t*)(mapped + sizeof(UILayer) + sizeof(UIString)*input->num_strings + sizeof(UIDrawable)*input->num_drawables);
|
|
|
|
|
uint32_t* codes = (uint32_t*)(mapped + sizeof(GPULayer) + sizeof(GPUString)*input->num_strings + sizeof(GPUDrawable)*input->num_drawables);
|
|
|
|
|
for(uint32_t i = 0; i < input->num_codes; i++) {
|
|
|
|
|
codes[i] = input->codes[i];
|
|
|
|
|
container->layers[index].codes_buffer[i] = input->codes[i];
|
|
|
|
|
}
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].codes, sizeof(UILayer) + sizeof(UIString)*input->num_strings + sizeof(UIDrawable)*input->num_drawables, 0, sizeof(uint32_t)*input->num_codes);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, container->layers[index].codes, sizeof(GPULayer) + sizeof(GPUString)*input->num_strings + sizeof(GPUDrawable)*input->num_drawables, 0, sizeof(uint32_t)*input->num_codes);
|
|
|
|
|
}
|
|
|
|
|
VK_RESULT(command_end_single(gpu->device, command_buffer, gpu->transfer_pool, gpu->transfer_queue));
|
|
|
|
|
vkQueueWaitIdle(gpu->transfer_queue.handle);
|
|
|
|
@ -422,7 +422,7 @@ VkResult create_layer(
|
|
|
|
|
VkResult load_texture(
|
|
|
|
|
const char* png_path,
|
|
|
|
|
RenderContext* gpu,
|
|
|
|
|
UIContextStorage* context,
|
|
|
|
|
UIContext* context,
|
|
|
|
|
uint32_t* index) {
|
|
|
|
|
*index = 0xFFFFFFFF;
|
|
|
|
|
for(uint32_t i = 0; i < context->max_textures; i++) {
|
|
|
|
@ -636,7 +636,7 @@ VkResult load_font(
|
|
|
|
|
VkBool32 antialias,
|
|
|
|
|
FT_Library library,
|
|
|
|
|
RenderContext* gpu,
|
|
|
|
|
UIContextStorage* context,
|
|
|
|
|
UIContext* context,
|
|
|
|
|
uint32_t* index){
|
|
|
|
|
FT_Face face;
|
|
|
|
|
|
|
|
|
@ -669,8 +669,8 @@ VkResult load_font(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t* tmp_charmap = malloc(sizeof(uint32_t)*face->num_glyphs);
|
|
|
|
|
SymbolInfo* symbols = malloc(sizeof(SymbolInfo)*face->num_glyphs);
|
|
|
|
|
Font info;
|
|
|
|
|
GPUSymbol* symbols = malloc(sizeof(GPUSymbol)*face->num_glyphs);
|
|
|
|
|
GPUFont info;
|
|
|
|
|
|
|
|
|
|
uint32_t glyph_index;
|
|
|
|
|
uint32_t max_height = 0;
|
|
|
|
@ -735,7 +735,7 @@ VkResult load_font(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(SymbolInfo)*info.num_symbols, &context->fonts[*index].symbols, &context->fonts[*index].symbol_memory));
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUSymbol)*info.num_symbols, &context->fonts[*index].symbols, &context->fonts[*index].symbol_memory));
|
|
|
|
|
|
|
|
|
|
VkImageCreateInfo image_info = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
|
|
@ -761,20 +761,20 @@ VkResult load_font(
|
|
|
|
|
VkBuffer transfer;
|
|
|
|
|
VmaAllocation transfer_memory;
|
|
|
|
|
void* mapped;
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(Font) + image_size*info.num_symbols + sizeof(SymbolInfo)*info.num_symbols, &transfer, &transfer_memory, &mapped));
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(GPUFont) + image_size*info.num_symbols + sizeof(GPUSymbol)*info.num_symbols, &transfer, &transfer_memory, &mapped));
|
|
|
|
|
|
|
|
|
|
info.symbol_list = buffer_address(gpu->device, context->fonts[*index].symbols);
|
|
|
|
|
|
|
|
|
|
memcpy(mapped, images, image_size*info.num_symbols);
|
|
|
|
|
memcpy(mapped + image_size*info.num_symbols, &info, sizeof(Font));
|
|
|
|
|
memcpy(mapped + image_size*info.num_symbols + sizeof(Font), symbols, sizeof(SymbolInfo)*info.num_symbols);
|
|
|
|
|
memcpy(mapped + image_size*info.num_symbols, &info, sizeof(GPUFont));
|
|
|
|
|
memcpy(mapped + image_size*info.num_symbols + sizeof(GPUFont), symbols, sizeof(GPUSymbol)*info.num_symbols);
|
|
|
|
|
|
|
|
|
|
free(images);
|
|
|
|
|
free(symbols);
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->font_infos, image_size*info.num_symbols, *index*sizeof(Font), sizeof(Font));
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->fonts[*index].symbols, image_size*info.num_symbols + sizeof(Font), 0, sizeof(SymbolInfo)*info.num_symbols);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->font_infos, image_size*info.num_symbols, *index*sizeof(GPUFont), sizeof(GPUFont));
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->fonts[*index].symbols, image_size*info.num_symbols + sizeof(GPUFont), 0, sizeof(GPUSymbol)*info.num_symbols);
|
|
|
|
|
|
|
|
|
|
VkImageMemoryBarrier first_barrier = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
|
|
@ -1063,48 +1063,48 @@ VkResult create_ui_context(
|
|
|
|
|
uint32_t max_textures,
|
|
|
|
|
uint32_t max_containers,
|
|
|
|
|
RenderContext* gpu,
|
|
|
|
|
UIContextStorage* memory) {
|
|
|
|
|
UIContext* context) {
|
|
|
|
|
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(UIContext), &memory->context, &memory->context_memory));
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(Font)*max_fonts, &memory->font_infos, &memory->font_infos_memory));
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUUIContext), &context->context, &context->context_memory));
|
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUFont)*max_fonts, &context->font_infos, &context->font_infos_memory));
|
|
|
|
|
|
|
|
|
|
VkBuffer transfer;
|
|
|
|
|
VmaAllocation transfer_memory;
|
|
|
|
|
VmaAllocation transfer_context;
|
|
|
|
|
UIContext* mapped;
|
|
|
|
|
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(UIContext), &transfer, &transfer_memory, (void**)&mapped));
|
|
|
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(UIContext), &transfer, &transfer_context, (void**)&mapped));
|
|
|
|
|
|
|
|
|
|
memory->data.font_infos = buffer_address(gpu->device, memory->font_infos);
|
|
|
|
|
memory->data.screen[0] = gpu->window_scale[0] / gpu->swapchain_extent.width;
|
|
|
|
|
memory->data.screen[1] = gpu->window_scale[1] / gpu->swapchain_extent.height;
|
|
|
|
|
memory->data.extent[0] = gpu->swapchain_extent.width / gpu->window_scale[0];
|
|
|
|
|
memory->data.extent[1] = gpu->swapchain_extent.height / gpu->window_scale[1];
|
|
|
|
|
memory->data.scale[0] = gpu->window_scale[0];
|
|
|
|
|
memory->data.scale[1] = gpu->window_scale[1];
|
|
|
|
|
memcpy(mapped, &memory->data, sizeof(UIContext));
|
|
|
|
|
context->data.font_infos = buffer_address(gpu->device, context->font_infos);
|
|
|
|
|
context->data.screen[0] = gpu->window_scale[0] / gpu->swapchain_extent.width;
|
|
|
|
|
context->data.screen[1] = gpu->window_scale[1] / gpu->swapchain_extent.height;
|
|
|
|
|
context->data.extent[0] = gpu->swapchain_extent.width / gpu->window_scale[0];
|
|
|
|
|
context->data.extent[1] = gpu->swapchain_extent.height / gpu->window_scale[1];
|
|
|
|
|
context->data.scale[0] = gpu->window_scale[0];
|
|
|
|
|
context->data.scale[1] = gpu->window_scale[1];
|
|
|
|
|
memcpy(mapped, &context->data, sizeof(GPUUIContext));
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, memory->context, 0, 0, sizeof(UIContext));
|
|
|
|
|
command_copy_buffer(command_buffer, transfer, context->context, 0, 0, sizeof(GPUUIContext));
|
|
|
|
|
VK_RESULT(command_end_single(gpu->device, command_buffer, gpu->transfer_pool, gpu->transfer_queue));
|
|
|
|
|
destroy_transfer_buffer(gpu->allocator, transfer, transfer_memory);
|
|
|
|
|
memory->address = buffer_address(gpu->device, memory->context);
|
|
|
|
|
destroy_transfer_buffer(gpu->allocator, transfer, transfer_context);
|
|
|
|
|
context->address = buffer_address(gpu->device, context->context);
|
|
|
|
|
|
|
|
|
|
VK_RESULT(create_ui_descriptor(gpu->device, max_fonts, max_textures, &memory->samplers_layout, &memory->textures_layout, &memory->samplers, &memory->textures, &memory->font_samplers, &memory->font_textures, &memory->fonts_pool, &memory->textures_pool));
|
|
|
|
|
VK_RESULT(create_ui_descriptor(gpu->device, max_fonts, max_textures, &context->samplers_layout, &context->textures_layout, &context->samplers, &context->textures, &context->font_samplers, &context->font_textures, &context->fonts_pool, &context->textures_pool));
|
|
|
|
|
|
|
|
|
|
VK_RESULT(create_ui_pipeline(gpu->device, gpu->render_pass, memory->samplers_layout, memory->textures_layout, &memory->pipeline, &memory->string_pipeline));
|
|
|
|
|
VK_RESULT(create_ui_pipeline(gpu->device, gpu->render_pass, context->samplers_layout, context->textures_layout, &context->pipeline, &context->string_pipeline));
|
|
|
|
|
|
|
|
|
|
memory->max_textures = max_textures;
|
|
|
|
|
memory->max_fonts = max_fonts;
|
|
|
|
|
memory->max_containers = max_containers;
|
|
|
|
|
context->max_textures = max_textures;
|
|
|
|
|
context->max_fonts = max_fonts;
|
|
|
|
|
context->max_containers = max_containers;
|
|
|
|
|
|
|
|
|
|
memory->texture_slots = malloc(max_textures*sizeof(TextureStorage));
|
|
|
|
|
memset(memory->texture_slots, 0, max_textures*sizeof(TextureStorage));
|
|
|
|
|
memory->fonts = malloc(max_fonts*sizeof(FontStorage));
|
|
|
|
|
memset(memory->fonts, 0, max_fonts*sizeof(FontStorage));
|
|
|
|
|
memory->containers = malloc(max_containers*sizeof(UIContainerStorage));
|
|
|
|
|
memset(memory->containers, 0, max_containers*sizeof(UIContainerStorage));
|
|
|
|
|
context->texture_slots = malloc(max_textures*sizeof(Texture));
|
|
|
|
|
memset(context->texture_slots, 0, max_textures*sizeof(Texture));
|
|
|
|
|
context->fonts = malloc(max_fonts*sizeof(Font));
|
|
|
|
|
memset(context->fonts, 0, max_fonts*sizeof(Font));
|
|
|
|
|
context->containers = malloc(max_containers*sizeof(Container));
|
|
|
|
|
memset(context->containers, 0, max_containers*sizeof(Container));
|
|
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|