/* * Copyright © 2019 Raspberry Pi * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "v3dv_private.h" #include "broadcom/cle/v3dx_pack.h" void v3dv_cmd_buffer_add_bo(struct v3dv_cmd_buffer *cmd_buffer, struct v3dv_bo *bo) { if (!bo) return; if (_mesa_set_search(cmd_buffer->bos, bo)) return; _mesa_set_add(cmd_buffer->bos, bo); } VkResult v3dv_CreateCommandPool(VkDevice _device, const VkCommandPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkCommandPool *pCmdPool) { V3DV_FROM_HANDLE(v3dv_device, device, _device); struct v3dv_cmd_pool *pool; /* We only support one queue */ assert(pCreateInfo->queueFamilyIndex == 0); pool = vk_alloc2(&device->alloc, pAllocator, sizeof(*pool), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (pool == NULL) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); if (pAllocator) pool->alloc = *pAllocator; else pool->alloc = device->alloc; list_inithead(&pool->cmd_buffers); *pCmdPool = v3dv_cmd_pool_to_handle(pool); return VK_SUCCESS; } static VkResult cmd_buffer_create(struct v3dv_device *device, struct v3dv_cmd_pool *pool, VkCommandBufferLevel level, VkCommandBuffer *pCommandBuffer) { struct v3dv_cmd_buffer *cmd_buffer; cmd_buffer = vk_zalloc(&pool->alloc, sizeof(*cmd_buffer), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (cmd_buffer == NULL) return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY); cmd_buffer->_loader_data.loaderMagic = ICD_LOADER_MAGIC; cmd_buffer->device = device; cmd_buffer->pool = pool; cmd_buffer->level = level; cmd_buffer->usage_flags = 0; v3dv_cl_init(cmd_buffer, &cmd_buffer->bcl); v3dv_cl_init(cmd_buffer, &cmd_buffer->rcl); v3dv_cl_init(cmd_buffer, &cmd_buffer->indirect); cmd_buffer->bos = _mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal); cmd_buffer->status = V3DV_CMD_BUFFER_STATUS_NEW; assert(pool); list_addtail(&cmd_buffer->pool_link, &pool->cmd_buffers); *pCommandBuffer = v3dv_cmd_buffer_to_handle(cmd_buffer); return VK_SUCCESS; } static void cmd_buffer_destroy(struct v3dv_cmd_buffer *cmd_buffer) { list_del(&cmd_buffer->pool_link); v3dv_cl_destroy(&cmd_buffer->bcl); v3dv_cl_destroy(&cmd_buffer->rcl); v3dv_cl_destroy(&cmd_buffer->indirect); /* Since we don't ref BOs, when we add them to the command buffer, don't * unref them here either. */ #if 0 set_foreach(cmd_buffer->bos, entry) { struct v3dv_bo *bo = (struct v3dv_bo *)entry->key; v3dv_bo_free(cmd_buffer->device, bo); } #endif _mesa_set_destroy(cmd_buffer->bos, NULL); v3dv_bo_free(cmd_buffer->device, cmd_buffer->tile_alloc); v3dv_bo_free(cmd_buffer->device, cmd_buffer->tile_state); vk_free(&cmd_buffer->pool->alloc, cmd_buffer); } static VkResult cmd_buffer_reset(struct v3dv_cmd_buffer *cmd_buffer) { if (cmd_buffer->status != V3DV_CMD_BUFFER_STATUS_INITIALIZED) { cmd_buffer->usage_flags = 0; v3dv_cl_reset(&cmd_buffer->bcl); v3dv_cl_reset(&cmd_buffer->rcl); v3dv_cl_reset(&cmd_buffer->indirect); cmd_buffer->status = V3DV_CMD_BUFFER_STATUS_INITIALIZED; } return VK_SUCCESS; } VkResult v3dv_AllocateCommandBuffers(VkDevice _device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers) { V3DV_FROM_HANDLE(v3dv_device, device, _device); V3DV_FROM_HANDLE(v3dv_cmd_pool, pool, pAllocateInfo->commandPool); /* FIXME: implement secondary command buffers */ assert(pAllocateInfo->level == VK_COMMAND_BUFFER_LEVEL_PRIMARY); VkResult result = VK_SUCCESS; uint32_t i; for (i = 0; i < pAllocateInfo->commandBufferCount; i++) { result = cmd_buffer_create(device, pool, pAllocateInfo->level, &pCommandBuffers[i]); if (result != VK_SUCCESS) break; } if (result != VK_SUCCESS) { v3dv_FreeCommandBuffers(_device, pAllocateInfo->commandPool, i, pCommandBuffers); for (i = 0; i < pAllocateInfo->commandBufferCount; i++) pCommandBuffers[i] = VK_NULL_HANDLE; } return result; } void v3dv_FreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers) { for (uint32_t i = 0; i < commandBufferCount; i++) { V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, pCommandBuffers[i]); if (!cmd_buffer) continue; cmd_buffer_destroy(cmd_buffer); } } void v3dv_DestroyCommandPool(VkDevice _device, VkCommandPool commandPool, const VkAllocationCallbacks *pAllocator) { V3DV_FROM_HANDLE(v3dv_device, device, _device); V3DV_FROM_HANDLE(v3dv_cmd_pool, pool, commandPool); if (!pool) return; list_for_each_entry_safe(struct v3dv_cmd_buffer, cmd_buffer, &pool->cmd_buffers, pool_link) { cmd_buffer_destroy(cmd_buffer); } vk_free2(&device->alloc, pAllocator, pool); } VkResult v3dv_BeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo *pBeginInfo) { V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer); assert(cmd_buffer->level == VK_COMMAND_BUFFER_LEVEL_SECONDARY || !(cmd_buffer->usage_flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)); /* If this is the first vkBeginCommandBuffer, we must initialize the * command buffer's state. Otherwise, we must reset its state. In both * cases we reset it. */ VkResult result = cmd_buffer_reset(cmd_buffer); if (result != VK_SUCCESS) return result; assert(cmd_buffer->status == V3DV_CMD_BUFFER_STATUS_INITIALIZED); cmd_buffer->usage_flags = pBeginInfo->flags; v3dv_cl_begin(&cmd_buffer->bcl); v3dv_cl_begin(&cmd_buffer->rcl); v3dv_cl_begin(&cmd_buffer->indirect); cmd_buffer->status = V3DV_CMD_BUFFER_STATUS_RECORDING; return VK_SUCCESS; } void v3dv_CmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, VkSubpassContents contents) { V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, commandBuffer); V3DV_FROM_HANDLE(v3dv_render_pass, pass, pRenderPassBegin->renderPass); V3DV_FROM_HANDLE(v3dv_framebuffer, framebuffer, pRenderPassBegin->framebuffer); cmd_buffer->state.pass = pass; cmd_buffer->state.framebuffer = framebuffer; v3dv_cl_ensure_space_with_branch(&cmd_buffer->bcl, 256); /* The PTB will request the tile alloc initial size per tile at start * of tile binning. */ const uint32_t fb_layers = 1; /* FIXME */ uint32_t tile_alloc_size = 64 * MAX2(fb_layers, 1) * framebuffer->draw_tiles_x * framebuffer->draw_tiles_y; /* The PTB allocates in aligned 4k chunks after the initial setup. */ tile_alloc_size = align(tile_alloc_size, 4096); /* Include the first two chunk allocations that the PTB does so that * we definitely clear the OOM condition before triggering one (the HW * won't trigger OOM during the first allocations). */ tile_alloc_size += 8192; /* For performance, allocate some extra initial memory after the PTB's * minimal allocations, so that we hopefully don't have to block the * GPU on the kernel handling an OOM signal. */ tile_alloc_size += 512 * 1024; cmd_buffer->tile_alloc = v3dv_bo_alloc(cmd_buffer->device, tile_alloc_size); v3dv_cmd_buffer_add_bo(cmd_buffer, cmd_buffer->tile_alloc); const uint32_t tsda_per_tile_size = 256; const uint32_t tile_state_size = MAX2(fb_layers, 1) * framebuffer->draw_tiles_x * framebuffer->draw_tiles_y * tsda_per_tile_size; cmd_buffer->tile_state = v3dv_bo_alloc(cmd_buffer->device, tile_state_size); v3dv_cmd_buffer_add_bo(cmd_buffer, cmd_buffer->tile_state); /* This must go before the binning mode configuration. It is * required for layered framebuffers to work. */ if (fb_layers > 0) { cl_emit(&cmd_buffer->bcl, NUMBER_OF_LAYERS, config) { config.number_of_layers = fb_layers; } } cl_emit(&cmd_buffer->bcl, TILE_BINNING_MODE_CFG, config) { config.width_in_pixels = framebuffer->width; config.height_in_pixels = framebuffer->height; config.number_of_render_targets = MAX2(framebuffer->attachment_count, 1); config.multisample_mode_4x = false; /* FIXME */ config.maximum_bpp_of_all_render_targets = framebuffer->internal_bpp; } /* There's definitely nothing in the VCD cache we want. */ cl_emit(&cmd_buffer->bcl, FLUSH_VCD_CACHE, bin); /* Disable any leftover OQ state from another job. */ cl_emit(&cmd_buffer->bcl, OCCLUSION_QUERY_COUNTER, counter); /* "Binning mode lists must have a Start Tile Binning item (6) after * any prefix state data before the binning list proper starts." */ cl_emit(&cmd_buffer->bcl, START_TILE_BINNING, bin); } void v3dv_CmdEndRenderPass(VkCommandBuffer commandBuffer) { } VkResult v3dv_EndCommandBuffer(VkCommandBuffer commandBuffer) { return VK_SUCCESS; }