mesa/src/imagination/vulkan/pvr_job_context.c
Sarah Walker f286cab27b pvr: Move BRN 44079, 48492 and 66011 code into pvrsrvkm specific directory
The new kernel mode driver will provide the relevant information directly to
userspace, so this code is only required for pvrsrvkm.

Signed-off-by: Sarah Walker <sarah.walker@imgtec.com>
Reviewed-by: Karmjit Mahil <Karmjit.Mahil@imgtec.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17595>
2022-07-27 10:13:19 +00:00

1334 lines
47 KiB
C

/*
* Copyright © 2022 Imagination Technologies Ltd.
*
* 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 <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <vulkan/vulkan.h>
#include "hwdef/rogue_hw_utils.h"
#include "pvr_bo.h"
#include "pvr_cdm_load_sr.h"
#include "pvr_csb.h"
#include "pvr_job_context.h"
#include "pvr_pds.h"
#include "pvr_private.h"
#include "pvr_transfer_eot.h"
#include "pvr_types.h"
#include "pvr_vdm_load_sr.h"
#include "pvr_vdm_store_sr.h"
#include "pvr_winsys.h"
#include "util/macros.h"
#include "vk_alloc.h"
#include "vk_log.h"
/* TODO: Is there some way to ensure the Vulkan driver doesn't exceed this
* value when constructing the control stream?
*/
/* The VDM callstack is used by the hardware to implement control stream links
* with a return, i.e. sub-control streams/subroutines. This value specifies the
* maximum callstack depth.
*/
#define PVR_VDM_CALLSTACK_MAX_DEPTH 1U
#define ROGUE_PDS_TASK_PROGRAM_SIZE 256U
static VkResult pvr_ctx_reset_cmd_init(struct pvr_device *device,
struct pvr_reset_cmd *const reset_cmd)
{
const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
/* The reset framework depends on compute support in the hw. */
assert(PVR_HAS_FEATURE(dev_info, compute));
if (PVR_HAS_QUIRK(dev_info, 51764))
pvr_finishme("Missing reset support for brn51764");
if (PVR_HAS_QUIRK(dev_info, 58839))
pvr_finishme("Missing reset support for brn58839");
return VK_SUCCESS;
}
static void pvr_ctx_reset_cmd_fini(struct pvr_device *device,
struct pvr_reset_cmd *reset_cmd)
{
/* TODO: reset command cleanup. */
}
static VkResult pvr_pds_pt_store_program_create_and_upload(
struct pvr_device *device,
struct pvr_bo *pt_bo,
uint32_t pt_bo_size,
struct pvr_pds_upload *const pds_upload_out)
{
struct pvr_pds_stream_out_terminate_program program = { 0 };
const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info);
size_t staging_buffer_size;
uint32_t *staging_buffer;
uint32_t *data_buffer;
uint32_t *code_buffer;
VkResult result;
/* Check the bo size can be converted to dwords without any rounding. */
assert(pt_bo_size % 4 == 0);
program.pds_persistent_temp_size_to_store = pt_bo_size / 4;
program.dev_address_for_storing_persistent_temp = pt_bo->vma->dev_addr.addr;
pvr_pds_generate_stream_out_terminate_program(&program,
NULL,
PDS_GENERATE_SIZES,
dev_info);
staging_buffer_size = (program.stream_out_terminate_pds_data_size +
program.stream_out_terminate_pds_code_size) *
sizeof(*staging_buffer);
staging_buffer = vk_zalloc(&device->vk.alloc,
staging_buffer_size,
8,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!staging_buffer)
return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
data_buffer = staging_buffer;
code_buffer =
pvr_pds_generate_stream_out_terminate_program(&program,
data_buffer,
PDS_GENERATE_DATA_SEGMENT,
dev_info);
pvr_pds_generate_stream_out_terminate_program(&program,
code_buffer,
PDS_GENERATE_CODE_SEGMENT,
dev_info);
/* This PDS program is passed to the HW via the PPP state words. These only
* allow the data segment address to be specified and expect the code
* segment to immediately follow. Assume the code alignment is the same as
* the data.
*/
result =
pvr_gpu_upload_pds(device,
data_buffer,
program.stream_out_terminate_pds_data_size,
PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE),
code_buffer,
program.stream_out_terminate_pds_code_size,
PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE),
cache_line_size,
pds_upload_out);
vk_free(&device->vk.alloc, staging_buffer);
return result;
}
static VkResult pvr_pds_pt_resume_program_create_and_upload(
struct pvr_device *device,
struct pvr_bo *pt_bo,
uint32_t pt_bo_size,
struct pvr_pds_upload *const pds_upload_out)
{
struct pvr_pds_stream_out_init_program program = { 0 };
const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info);
size_t staging_buffer_size;
uint32_t *staging_buffer;
uint32_t *data_buffer;
uint32_t *code_buffer;
VkResult result;
/* Check the bo size can be converted to dwords without any rounding. */
assert(pt_bo_size % 4 == 0);
program.num_buffers = 1;
program.pds_buffer_data_size[0] = pt_bo_size / 4;
program.dev_address_for_buffer_data[0] = pt_bo->vma->dev_addr.addr;
pvr_pds_generate_stream_out_init_program(&program,
NULL,
false,
PDS_GENERATE_SIZES,
dev_info);
staging_buffer_size = (program.stream_out_init_pds_data_size +
program.stream_out_init_pds_code_size) *
sizeof(*staging_buffer);
staging_buffer = vk_zalloc(&device->vk.alloc,
staging_buffer_size,
8,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!staging_buffer)
return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
data_buffer = staging_buffer;
code_buffer =
pvr_pds_generate_stream_out_init_program(&program,
data_buffer,
false,
PDS_GENERATE_DATA_SEGMENT,
dev_info);
pvr_pds_generate_stream_out_init_program(&program,
code_buffer,
false,
PDS_GENERATE_CODE_SEGMENT,
dev_info);
/* This PDS program is passed to the HW via the PPP state words. These only
* allow the data segment address to be specified and expect the code
* segment to immediately follow. Assume the code alignment is the same as
* the data.
*/
result =
pvr_gpu_upload_pds(device,
data_buffer,
program.stream_out_init_pds_data_size,
PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE),
code_buffer,
program.stream_out_init_pds_code_size,
PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE),
cache_line_size,
pds_upload_out);
vk_free(&device->vk.alloc, staging_buffer);
return result;
}
static VkResult
pvr_render_job_pt_programs_setup(struct pvr_device *device,
struct rogue_pt_programs *pt_programs)
{
VkResult result;
result = pvr_bo_alloc(device,
device->heaps.pds_heap,
ROGUE_LLS_PDS_PERSISTENT_TEMPS_BUFFER_SIZE,
ROGUE_LLS_PDS_PERSISTENT_TEMPS_BUFFER_ALIGNMENT,
PVR_BO_ALLOC_FLAG_CPU_ACCESS,
&pt_programs->store_resume_state_bo);
if (result != VK_SUCCESS)
return result;
result = pvr_pds_pt_store_program_create_and_upload(
device,
pt_programs->store_resume_state_bo,
ROGUE_LLS_PDS_PERSISTENT_TEMPS_BUFFER_SIZE,
&pt_programs->pds_store_program);
if (result != VK_SUCCESS)
goto err_free_store_resume_state_bo;
result = pvr_pds_pt_resume_program_create_and_upload(
device,
pt_programs->store_resume_state_bo,
ROGUE_LLS_PDS_PERSISTENT_TEMPS_BUFFER_SIZE,
&pt_programs->pds_resume_program);
if (result != VK_SUCCESS)
goto err_free_pds_store_program;
return VK_SUCCESS;
err_free_pds_store_program:
pvr_bo_free(device, pt_programs->pds_store_program.pvr_bo);
err_free_store_resume_state_bo:
pvr_bo_free(device, pt_programs->store_resume_state_bo);
return result;
}
static void
pvr_render_job_pt_programs_cleanup(struct pvr_device *device,
struct rogue_pt_programs *pt_programs)
{
pvr_bo_free(device, pt_programs->pds_resume_program.pvr_bo);
pvr_bo_free(device, pt_programs->pds_store_program.pvr_bo);
pvr_bo_free(device, pt_programs->store_resume_state_bo);
}
static void pvr_pds_ctx_sr_program_setup(
bool cc_enable,
uint64_t usc_program_upload_offset,
uint8_t usc_temps,
pvr_dev_addr_t sr_addr,
struct pvr_pds_shared_storing_program *const program_out)
{
/* The PDS task is the same for stores and loads. */
*program_out = (struct pvr_pds_shared_storing_program){
.cc_enable = cc_enable,
.doutw_control = {
.dest_store = PDS_UNIFIED_STORE,
.num_const64 = 2,
.doutw_data = {
[0] = sr_addr.addr,
[1] = sr_addr.addr + ROGUE_LLS_SHARED_REGS_RESERVE_SIZE,
},
.last_instruction = false,
},
};
pvr_pds_setup_doutu(&program_out->usc_task.usc_task_control,
usc_program_upload_offset,
usc_temps,
PVRX(PDSINST_DOUTU_SAMPLE_RATE_INSTANCE),
false);
}
/* Note: pvr_pds_compute_ctx_sr_program_create_and_upload() is very similar to
* this. If there is a problem here it's likely that the same problem exists
* there so don't forget to update the compute function.
*/
static VkResult pvr_pds_render_ctx_sr_program_create_and_upload(
struct pvr_device *device,
uint64_t usc_program_upload_offset,
uint8_t usc_temps,
pvr_dev_addr_t sr_addr,
struct pvr_pds_upload *const pds_upload_out)
{
const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info);
const uint32_t pds_data_alignment =
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE) / 4U;
/* FIXME: pvr_pds_generate_shared_storing_program() doesn't return the data
* and code size when using the PDS_GENERATE_SIZES mode.
*/
STATIC_ASSERT(ROGUE_PDS_TASK_PROGRAM_SIZE % 4 == 0);
uint32_t staging_buffer[ROGUE_PDS_TASK_PROGRAM_SIZE / 4U] = { 0 };
struct pvr_pds_shared_storing_program program;
ASSERTED uint32_t *buffer_end;
uint32_t code_offset;
pvr_pds_ctx_sr_program_setup(false,
usc_program_upload_offset,
usc_temps,
sr_addr,
&program);
pvr_pds_generate_shared_storing_program(&program,
&staging_buffer[0],
PDS_GENERATE_DATA_SEGMENT,
dev_info);
code_offset = ALIGN_POT(program.data_size, pds_data_alignment);
buffer_end =
pvr_pds_generate_shared_storing_program(&program,
&staging_buffer[code_offset],
PDS_GENERATE_CODE_SEGMENT,
dev_info);
assert((uint32_t)(buffer_end - staging_buffer) * 4 <
ROGUE_PDS_TASK_PROGRAM_SIZE);
return pvr_gpu_upload_pds(device,
&staging_buffer[0],
program.data_size,
PVRX(VDMCTRL_PDS_STATE1_PDS_DATA_ADDR_ALIGNMENT),
&staging_buffer[code_offset],
program.code_size,
PVRX(VDMCTRL_PDS_STATE2_PDS_CODE_ADDR_ALIGNMENT),
cache_line_size,
pds_upload_out);
}
/* Note: pvr_pds_render_ctx_sr_program_create_and_upload() is very similar to
* this. If there is a problem here it's likely that the same problem exists
* there so don't forget to update the render_ctx function.
*/
static VkResult pvr_pds_compute_ctx_sr_program_create_and_upload(
struct pvr_device *device,
bool is_loading_program,
uint64_t usc_program_upload_offset,
uint8_t usc_temps,
pvr_dev_addr_t sr_addr,
struct pvr_pds_upload *const pds_upload_out)
{
const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info);
const uint32_t pds_data_alignment =
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE) / 4U;
/* FIXME: pvr_pds_generate_shared_storing_program() doesn't return the data
* and code size when using the PDS_GENERATE_SIZES mode.
*/
STATIC_ASSERT(ROGUE_PDS_TASK_PROGRAM_SIZE % 4 == 0);
uint32_t staging_buffer[ROGUE_PDS_TASK_PROGRAM_SIZE / 4U] = { 0 };
struct pvr_pds_shared_storing_program program;
uint32_t *buffer_ptr;
uint32_t code_offset;
pvr_pds_ctx_sr_program_setup(PVR_HAS_ERN(dev_info, 35421),
usc_program_upload_offset,
usc_temps,
sr_addr,
&program);
if (is_loading_program && PVR_NEED_SW_COMPUTE_PDS_BARRIER(dev_info)) {
pvr_pds_generate_compute_shared_loading_program(&program,
&staging_buffer[0],
PDS_GENERATE_DATA_SEGMENT,
dev_info);
} else {
pvr_pds_generate_shared_storing_program(&program,
&staging_buffer[0],
PDS_GENERATE_DATA_SEGMENT,
dev_info);
}
code_offset = ALIGN_POT(program.data_size, pds_data_alignment);
buffer_ptr =
pvr_pds_generate_compute_barrier_conditional(&staging_buffer[code_offset],
PDS_GENERATE_CODE_SEGMENT);
if (is_loading_program && PVR_NEED_SW_COMPUTE_PDS_BARRIER(dev_info)) {
buffer_ptr = pvr_pds_generate_compute_shared_loading_program(
&program,
buffer_ptr,
PDS_GENERATE_CODE_SEGMENT,
dev_info);
} else {
buffer_ptr =
pvr_pds_generate_shared_storing_program(&program,
buffer_ptr,
PDS_GENERATE_CODE_SEGMENT,
dev_info);
}
assert((uint32_t)(buffer_ptr - staging_buffer) * 4 <
ROGUE_PDS_TASK_PROGRAM_SIZE);
STATIC_ASSERT(PVRX(CR_CDM_CONTEXT_PDS0_DATA_ADDR_ALIGNMENT) ==
PVRX(CR_CDM_CONTEXT_LOAD_PDS0_DATA_ADDR_ALIGNMENT));
STATIC_ASSERT(PVRX(CR_CDM_CONTEXT_PDS0_CODE_ADDR_ALIGNMENT) ==
PVRX(CR_CDM_CONTEXT_LOAD_PDS0_CODE_ADDR_ALIGNMENT));
return pvr_gpu_upload_pds(
device,
&staging_buffer[0],
program.data_size,
PVRX(CR_CDM_CONTEXT_PDS0_DATA_ADDR_ALIGNMENT),
&staging_buffer[code_offset],
(uint32_t)(buffer_ptr - &staging_buffer[code_offset]),
PVRX(CR_CDM_CONTEXT_PDS0_CODE_ADDR_ALIGNMENT),
cache_line_size,
pds_upload_out);
}
enum pvr_ctx_sr_program_target {
PVR_CTX_SR_RENDER_TARGET,
PVR_CTX_SR_COMPUTE_TARGET,
};
static VkResult pvr_ctx_sr_programs_setup(struct pvr_device *device,
enum pvr_ctx_sr_program_target target,
struct rogue_sr_programs *sr_programs)
{
const uint64_t store_load_state_bo_size =
PVRX(LLS_USC_SHARED_REGS_BUFFER_SIZE) +
ROGUE_LLS_SHARED_REGS_RESERVE_SIZE;
const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info);
uint64_t usc_store_program_upload_offset;
uint64_t usc_load_program_upload_offset;
const uint8_t *usc_load_sr_code;
uint32_t usc_load_sr_code_size;
VkResult result;
/* Note that this is being used for both compute and render ctx. There is no
* compute equivalent define for the VDMCTRL unit size.
*/
/* 4 blocks (16 dwords / 64 bytes) in USC to prevent fragmentation. */
sr_programs->usc.unified_size =
DIV_ROUND_UP(64, PVRX(VDMCTRL_PDS_STATE0_USC_UNIFIED_SIZE_UNIT_SIZE));
result = pvr_bo_alloc(device,
device->heaps.pds_heap,
store_load_state_bo_size,
cache_line_size,
PVR_WINSYS_BO_FLAG_CPU_ACCESS,
&sr_programs->store_load_state_bo);
if (result != VK_SUCCESS)
return result;
/* USC state update: SR state store. */
assert(sizeof(pvr_vdm_store_sr_code) < ROGUE_USC_TASK_PROGRAM_SIZE);
result = pvr_gpu_upload_usc(device,
pvr_vdm_store_sr_code,
sizeof(pvr_vdm_store_sr_code),
cache_line_size,
&sr_programs->usc.store_program_bo);
if (result != VK_SUCCESS)
goto err_free_store_load_state_bo;
usc_store_program_upload_offset =
sr_programs->usc.store_program_bo->vma->dev_addr.addr -
device->heaps.usc_heap->base_addr.addr;
/* USC state update: SR state load. */
if (target == PVR_CTX_SR_COMPUTE_TARGET && PVR_HAS_QUIRK(dev_info, 62269)) {
STATIC_ASSERT(sizeof(pvr_cdm_load_sr_code) < ROGUE_USC_TASK_PROGRAM_SIZE);
usc_load_sr_code = pvr_cdm_load_sr_code;
usc_load_sr_code_size = sizeof(pvr_cdm_load_sr_code);
} else {
STATIC_ASSERT(sizeof(pvr_vdm_load_sr_code) < ROGUE_USC_TASK_PROGRAM_SIZE);
usc_load_sr_code = pvr_vdm_load_sr_code;
usc_load_sr_code_size = sizeof(pvr_vdm_load_sr_code);
}
result = pvr_gpu_upload_usc(device,
usc_load_sr_code,
usc_load_sr_code_size,
cache_line_size,
&sr_programs->usc.load_program_bo);
if (result != VK_SUCCESS)
goto err_free_usc_store_program_bo;
usc_load_program_upload_offset =
sr_programs->usc.load_program_bo->vma->dev_addr.addr -
device->heaps.usc_heap->base_addr.addr;
/* FIXME: The number of USC temps should be output alongside
* pvr_vdm_store_sr_code rather than hard coded.
*/
/* Create and upload the PDS load and store programs. Point them to the
* appropriate USC load and store programs.
*/
switch (target) {
case PVR_CTX_SR_RENDER_TARGET:
/* PDS state update: SR state store. */
result = pvr_pds_render_ctx_sr_program_create_and_upload(
device,
usc_store_program_upload_offset,
8,
sr_programs->store_load_state_bo->vma->dev_addr,
&sr_programs->pds.store_program);
if (result != VK_SUCCESS)
goto err_free_usc_load_program_bo;
/* PDS state update: SR state load. */
result = pvr_pds_render_ctx_sr_program_create_and_upload(
device,
usc_load_program_upload_offset,
20,
sr_programs->store_load_state_bo->vma->dev_addr,
&sr_programs->pds.load_program);
if (result != VK_SUCCESS)
goto err_free_pds_store_program_bo;
break;
case PVR_CTX_SR_COMPUTE_TARGET:
/* PDS state update: SR state store. */
result = pvr_pds_compute_ctx_sr_program_create_and_upload(
device,
false,
usc_store_program_upload_offset,
8,
sr_programs->store_load_state_bo->vma->dev_addr,
&sr_programs->pds.store_program);
if (result != VK_SUCCESS)
goto err_free_usc_load_program_bo;
/* PDS state update: SR state load. */
result = pvr_pds_compute_ctx_sr_program_create_and_upload(
device,
true,
usc_load_program_upload_offset,
20,
sr_programs->store_load_state_bo->vma->dev_addr,
&sr_programs->pds.load_program);
if (result != VK_SUCCESS)
goto err_free_pds_store_program_bo;
break;
default:
unreachable("Invalid target.");
break;
}
return VK_SUCCESS;
err_free_pds_store_program_bo:
pvr_bo_free(device, sr_programs->pds.store_program.pvr_bo);
err_free_usc_load_program_bo:
pvr_bo_free(device, sr_programs->usc.load_program_bo);
err_free_usc_store_program_bo:
pvr_bo_free(device, sr_programs->usc.store_program_bo);
err_free_store_load_state_bo:
pvr_bo_free(device, sr_programs->store_load_state_bo);
return VK_SUCCESS;
}
static void pvr_ctx_sr_programs_cleanup(struct pvr_device *device,
struct rogue_sr_programs *sr_programs)
{
pvr_bo_free(device, sr_programs->pds.load_program.pvr_bo);
pvr_bo_free(device, sr_programs->pds.store_program.pvr_bo);
pvr_bo_free(device, sr_programs->usc.load_program_bo);
pvr_bo_free(device, sr_programs->usc.store_program_bo);
pvr_bo_free(device, sr_programs->store_load_state_bo);
}
static VkResult
pvr_render_ctx_switch_programs_setup(struct pvr_device *device,
struct pvr_render_ctx_programs *programs)
{
VkResult result;
result = pvr_render_job_pt_programs_setup(device, &programs->pt);
if (result != VK_SUCCESS)
return result;
result = pvr_ctx_sr_programs_setup(device,
PVR_CTX_SR_RENDER_TARGET,
&programs->sr);
if (result != VK_SUCCESS)
goto err_pt_programs_cleanup;
return VK_SUCCESS;
err_pt_programs_cleanup:
pvr_render_job_pt_programs_cleanup(device, &programs->pt);
return result;
}
static void
pvr_render_ctx_switch_programs_cleanup(struct pvr_device *device,
struct pvr_render_ctx_programs *programs)
{
pvr_ctx_sr_programs_cleanup(device, &programs->sr);
pvr_render_job_pt_programs_cleanup(device, &programs->pt);
}
static VkResult pvr_render_ctx_switch_init(struct pvr_device *device,
struct pvr_render_ctx *ctx)
{
struct pvr_render_ctx_switch *ctx_switch = &ctx->ctx_switch;
const uint64_t vdm_state_bo_flags = PVR_BO_ALLOC_FLAG_GPU_UNCACHED |
PVR_BO_ALLOC_FLAG_CPU_ACCESS;
const uint64_t geom_state_bo_flags = PVR_BO_ALLOC_FLAG_GPU_UNCACHED |
PVR_BO_ALLOC_FLAG_CPU_ACCESS;
VkResult result;
result = pvr_bo_alloc(device,
device->heaps.general_heap,
ROGUE_LLS_VDM_CONTEXT_RESUME_BUFFER_SIZE,
ROGUE_LLS_VDM_CONTEXT_RESUME_BUFFER_ALIGNMENT,
vdm_state_bo_flags,
&ctx_switch->vdm_state_bo);
if (result != VK_SUCCESS)
return result;
result = pvr_bo_alloc(device,
device->heaps.general_heap,
ROGUE_LLS_TA_STATE_BUFFER_SIZE,
ROGUE_LLS_TA_STATE_BUFFER_ALIGNMENT,
geom_state_bo_flags,
&ctx_switch->geom_state_bo);
if (result != VK_SUCCESS)
goto err_pvr_bo_free_vdm_state_bo;
for (uint32_t i = 0; i < ARRAY_SIZE(ctx_switch->programs); i++) {
result =
pvr_render_ctx_switch_programs_setup(device, &ctx_switch->programs[i]);
if (result)
goto err_programs_cleanup;
}
return result;
err_programs_cleanup:
for (uint32_t i = 0; i < ARRAY_SIZE(ctx_switch->programs); i++) {
pvr_render_ctx_switch_programs_cleanup(device, &ctx_switch->programs[i]);
}
pvr_bo_free(device, ctx_switch->geom_state_bo);
err_pvr_bo_free_vdm_state_bo:
pvr_bo_free(device, ctx_switch->vdm_state_bo);
return result;
}
static void pvr_render_ctx_switch_fini(struct pvr_device *device,
struct pvr_render_ctx *ctx)
{
struct pvr_render_ctx_switch *ctx_switch = &ctx->ctx_switch;
for (uint32_t i = 0; i < ARRAY_SIZE(ctx_switch->programs); i++) {
pvr_render_ctx_switch_programs_cleanup(device, &ctx_switch->programs[i]);
}
pvr_bo_free(device, ctx_switch->geom_state_bo);
pvr_bo_free(device, ctx_switch->vdm_state_bo);
}
static void
pvr_rogue_get_vdmctrl_pds_state_words(struct pvr_pds_upload *pds_program,
enum PVRX(VDMCTRL_USC_TARGET) usc_target,
uint8_t usc_unified_size,
uint32_t *const state0_out,
uint32_t *const state1_out)
{
pvr_csb_pack (state0_out, VDMCTRL_PDS_STATE0, state) {
/* Convert the data size from dwords to bytes. */
const uint32_t pds_data_size = pds_program->data_size * 4;
state.dm_target = PVRX(VDMCTRL_DM_TARGET_VDM);
state.usc_target = usc_target;
state.usc_common_size = 0;
state.usc_unified_size = usc_unified_size;
state.pds_temp_size = 0;
assert(pds_data_size % PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE) ==
0);
state.pds_data_size =
pds_data_size / PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE);
};
pvr_csb_pack (state1_out, VDMCTRL_PDS_STATE1, state) {
state.pds_data_addr = PVR_DEV_ADDR(pds_program->data_offset);
state.sd_type = PVRX(VDMCTRL_SD_TYPE_PDS);
state.sd_next_type = PVRX(VDMCTRL_SD_TYPE_PDS);
}
}
static void
pvr_rogue_get_geom_state_stream_out_words(struct pvr_pds_upload *pds_program,
uint32_t *const stream_out1_out,
uint32_t *const stream_out2_out)
{
pvr_csb_pack (stream_out1_out, TA_STATE_STREAM_OUT1, state) {
/* Convert the data size from dwords to bytes. */
const uint32_t pds_data_size = pds_program->data_size * 4;
state.sync = true;
assert(pds_data_size %
PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE) ==
0);
state.pds_data_size =
pds_data_size / PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE);
state.pds_temp_size = 0;
}
pvr_csb_pack (stream_out2_out, TA_STATE_STREAM_OUT2, state) {
state.pds_data_addr = PVR_DEV_ADDR(pds_program->data_offset);
}
}
static void pvr_render_ctx_ws_static_state_init(
struct pvr_render_ctx *ctx,
struct pvr_winsys_render_ctx_static_state *static_state)
{
uint64_t *q_dst;
uint32_t *d_dst;
q_dst = &static_state->vdm_ctx_state_base_addr;
pvr_csb_pack (q_dst, CR_VDM_CONTEXT_STATE_BASE, base) {
base.addr = ctx->ctx_switch.vdm_state_bo->vma->dev_addr;
}
q_dst = &static_state->geom_ctx_state_base_addr;
pvr_csb_pack (q_dst, CR_TA_CONTEXT_STATE_BASE, base) {
base.addr = ctx->ctx_switch.geom_state_bo->vma->dev_addr;
}
for (uint32_t i = 0; i < ARRAY_SIZE(ctx->ctx_switch.programs); i++) {
struct rogue_pt_programs *pt_prog = &ctx->ctx_switch.programs[i].pt;
struct rogue_sr_programs *sr_prog = &ctx->ctx_switch.programs[i].sr;
/* Context store state. */
q_dst = &static_state->geom_state[i].vdm_ctx_store_task0;
pvr_csb_pack (q_dst, CR_VDM_CONTEXT_STORE_TASK0, task0) {
pvr_rogue_get_vdmctrl_pds_state_words(&sr_prog->pds.store_program,
PVRX(VDMCTRL_USC_TARGET_ANY),
sr_prog->usc.unified_size,
&task0.pds_state0,
&task0.pds_state1);
}
d_dst = &static_state->geom_state[i].vdm_ctx_store_task1;
pvr_csb_pack (d_dst, CR_VDM_CONTEXT_STORE_TASK1, task1) {
pvr_csb_pack (&task1.pds_state2, VDMCTRL_PDS_STATE2, state) {
state.pds_code_addr =
PVR_DEV_ADDR(sr_prog->pds.store_program.code_offset);
}
}
q_dst = &static_state->geom_state[i].vdm_ctx_store_task2;
pvr_csb_pack (q_dst, CR_VDM_CONTEXT_STORE_TASK2, task2) {
pvr_rogue_get_geom_state_stream_out_words(&pt_prog->pds_store_program,
&task2.stream_out1,
&task2.stream_out2);
}
/* Context resume state. */
q_dst = &static_state->geom_state[i].vdm_ctx_resume_task0;
pvr_csb_pack (q_dst, CR_VDM_CONTEXT_RESUME_TASK0, task0) {
pvr_rogue_get_vdmctrl_pds_state_words(&sr_prog->pds.load_program,
PVRX(VDMCTRL_USC_TARGET_ALL),
sr_prog->usc.unified_size,
&task0.pds_state0,
&task0.pds_state1);
}
d_dst = &static_state->geom_state[i].vdm_ctx_resume_task1;
pvr_csb_pack (d_dst, CR_VDM_CONTEXT_RESUME_TASK1, task1) {
pvr_csb_pack (&task1.pds_state2, VDMCTRL_PDS_STATE2, state) {
state.pds_code_addr =
PVR_DEV_ADDR(sr_prog->pds.load_program.code_offset);
}
}
q_dst = &static_state->geom_state[i].vdm_ctx_resume_task2;
pvr_csb_pack (q_dst, CR_VDM_CONTEXT_RESUME_TASK2, task2) {
pvr_rogue_get_geom_state_stream_out_words(&pt_prog->pds_resume_program,
&task2.stream_out1,
&task2.stream_out2);
}
}
}
static void pvr_render_ctx_ws_create_info_init(
struct pvr_render_ctx *ctx,
enum pvr_winsys_ctx_priority priority,
struct pvr_winsys_render_ctx_create_info *create_info)
{
create_info->priority = priority;
create_info->vdm_callstack_addr = ctx->vdm_callstack_bo->vma->dev_addr;
pvr_render_ctx_ws_static_state_init(ctx, &create_info->static_state);
}
VkResult pvr_render_ctx_create(struct pvr_device *device,
enum pvr_winsys_ctx_priority priority,
struct pvr_render_ctx **const ctx_out)
{
const uint64_t vdm_callstack_size =
sizeof(uint64_t) * PVR_VDM_CALLSTACK_MAX_DEPTH;
struct pvr_winsys_render_ctx_create_info create_info;
struct pvr_render_ctx *ctx;
VkResult result;
ctx = vk_alloc(&device->vk.alloc,
sizeof(*ctx),
8,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!ctx)
return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
ctx->device = device;
result = pvr_bo_alloc(device,
device->heaps.general_heap,
vdm_callstack_size,
PVRX(CR_VDM_CALL_STACK_POINTER_ADDR_ALIGNMENT),
0,
&ctx->vdm_callstack_bo);
if (result != VK_SUCCESS)
goto err_vk_free_ctx;
result = pvr_render_ctx_switch_init(device, ctx);
if (result != VK_SUCCESS)
goto err_free_vdm_callstack_bo;
result = pvr_ctx_reset_cmd_init(device, &ctx->reset_cmd);
if (result != VK_SUCCESS)
goto err_render_ctx_switch_fini;
/* ctx must be fully initialized by this point since
* pvr_render_ctx_ws_create_info_init() depends on this.
*/
pvr_render_ctx_ws_create_info_init(ctx, priority, &create_info);
result = device->ws->ops->render_ctx_create(device->ws,
&create_info,
&ctx->ws_ctx);
if (result != VK_SUCCESS)
goto err_render_ctx_reset_cmd_fini;
*ctx_out = ctx;
return VK_SUCCESS;
err_render_ctx_reset_cmd_fini:
pvr_ctx_reset_cmd_fini(device, &ctx->reset_cmd);
err_render_ctx_switch_fini:
pvr_render_ctx_switch_fini(device, ctx);
err_free_vdm_callstack_bo:
pvr_bo_free(device, ctx->vdm_callstack_bo);
err_vk_free_ctx:
vk_free(&device->vk.alloc, ctx);
return result;
}
void pvr_render_ctx_destroy(struct pvr_render_ctx *ctx)
{
struct pvr_device *device = ctx->device;
device->ws->ops->render_ctx_destroy(ctx->ws_ctx);
pvr_ctx_reset_cmd_fini(device, &ctx->reset_cmd);
pvr_render_ctx_switch_fini(device, ctx);
pvr_bo_free(device, ctx->vdm_callstack_bo);
vk_free(&device->vk.alloc, ctx);
}
static VkResult pvr_pds_sr_fence_terminate_program_create_and_upload(
struct pvr_device *device,
struct pvr_pds_upload *const pds_upload_out)
{
const uint32_t pds_data_alignment =
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE) / 4U;
const struct pvr_device_runtime_info *dev_runtime_info =
&device->pdevice->dev_runtime_info;
ASSERTED const struct pvr_device_info *dev_info = &device->pdevice->dev_info;
uint32_t staging_buffer[PVRX(PDS_TASK_PROGRAM_SIZE) >> 2U];
struct pvr_pds_fence_program program = { 0 };
ASSERTED uint32_t *buffer_end;
uint32_t code_offset;
uint32_t data_size;
/* SW_COMPUTE_PDS_BARRIER is not supported with 2 or more phantoms. */
assert(!(PVR_NEED_SW_COMPUTE_PDS_BARRIER(dev_info) &&
dev_runtime_info->num_phantoms >= 2));
pvr_pds_generate_fence_terminate_program(&program,
staging_buffer,
PDS_GENERATE_DATA_SEGMENT,
&device->pdevice->dev_info);
/* FIXME: pvr_pds_generate_fence_terminate_program() zeros out the data_size
* when we generate the code segment. Implement
* PDS_GENERATE_CODEDATA_SEGMENTS? Or wait for the pds gen api to change?
* This behavior doesn't seem consistent with the rest of the api. For now
* we store the size in a variable.
*/
data_size = program.data_size;
code_offset = ALIGN_POT(program.data_size, pds_data_alignment);
buffer_end =
pvr_pds_generate_fence_terminate_program(&program,
&staging_buffer[code_offset],
PDS_GENERATE_CODE_SEGMENT,
&device->pdevice->dev_info);
assert((uint64_t)(buffer_end - staging_buffer) * 4U <
ROGUE_PDS_TASK_PROGRAM_SIZE);
return pvr_gpu_upload_pds(device,
staging_buffer,
data_size,
PVRX(CR_CDM_TERMINATE_PDS_DATA_ADDR_ALIGNMENT),
&staging_buffer[code_offset],
program.code_size,
PVRX(CR_CDM_TERMINATE_PDS_CODE_ADDR_ALIGNMENT),
0,
pds_upload_out);
}
static void pvr_compute_ctx_ws_static_state_init(
const struct pvr_device_info *const dev_info,
const struct pvr_compute_ctx *const ctx,
struct pvr_winsys_compute_ctx_static_state *const static_state)
{
const struct pvr_compute_ctx_switch *const ctx_switch = &ctx->ctx_switch;
/* CR_CDM_CONTEXT_... use state store program info. */
pvr_csb_pack (&static_state->cdm_ctx_store_pds0,
CR_CDM_CONTEXT_PDS0,
state) {
state.data_addr =
PVR_DEV_ADDR(ctx_switch->sr[0].pds.store_program.data_offset);
state.code_addr =
PVR_DEV_ADDR(ctx_switch->sr[0].pds.store_program.code_offset);
}
pvr_csb_pack (&static_state->cdm_ctx_store_pds0_b,
CR_CDM_CONTEXT_PDS0,
state) {
state.data_addr =
PVR_DEV_ADDR(ctx_switch->sr[1].pds.store_program.data_offset);
state.code_addr =
PVR_DEV_ADDR(ctx_switch->sr[1].pds.store_program.code_offset);
}
pvr_csb_pack (&static_state->cdm_ctx_store_pds1,
CR_CDM_CONTEXT_PDS1,
state) {
/* Convert the data size from dwords to bytes. */
const uint32_t store_program_data_size =
ctx_switch->sr[0].pds.store_program.data_size * 4U;
state.pds_seq_dep = true;
state.usc_seq_dep = false;
state.target = true;
state.unified_size = ctx_switch->sr[0].usc.unified_size;
state.common_shared = false;
state.common_size = 0;
state.temp_size = 0;
assert(store_program_data_size %
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE) ==
0);
state.data_size = store_program_data_size /
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE);
state.fence = true;
}
/* CR_CDM_TERMINATE_... use fence terminate info. */
pvr_csb_pack (&static_state->cdm_ctx_terminate_pds,
CR_CDM_TERMINATE_PDS,
state) {
state.data_addr =
PVR_DEV_ADDR(ctx_switch->sr_fence_terminate_program.data_offset);
state.code_addr =
PVR_DEV_ADDR(ctx_switch->sr_fence_terminate_program.code_offset);
}
pvr_csb_pack (&static_state->cdm_ctx_terminate_pds1,
CR_CDM_TERMINATE_PDS1,
state) {
/* Convert the data size from dwords to bytes. */
const uint32_t fence_terminate_program_data_size =
ctx_switch->sr_fence_terminate_program.data_size * 4U;
state.pds_seq_dep = true;
state.usc_seq_dep = false;
state.target = !PVR_HAS_FEATURE(dev_info, compute_morton_capable);
state.unified_size = 0;
/* Common store is for shareds -- this will free the partitions. */
state.common_shared = true;
state.common_size = 0;
state.temp_size = 0;
assert(fence_terminate_program_data_size %
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE) ==
0);
state.data_size = fence_terminate_program_data_size /
PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE);
state.fence = true;
}
/* CR_CDM_RESUME_... use state load program info. */
pvr_csb_pack (&static_state->cdm_ctx_resume_pds0,
CR_CDM_CONTEXT_LOAD_PDS0,
state) {
state.data_addr =
PVR_DEV_ADDR(ctx_switch->sr[0].pds.load_program.data_offset);
state.code_addr =
PVR_DEV_ADDR(ctx_switch->sr[0].pds.load_program.code_offset);
}
pvr_csb_pack (&static_state->cdm_ctx_resume_pds0_b,
CR_CDM_CONTEXT_LOAD_PDS0,
state) {
state.data_addr =
PVR_DEV_ADDR(ctx_switch->sr[1].pds.load_program.data_offset);
state.code_addr =
PVR_DEV_ADDR(ctx_switch->sr[1].pds.load_program.code_offset);
}
}
static void pvr_compute_ctx_ws_create_info_init(
const struct pvr_compute_ctx *const ctx,
enum pvr_winsys_ctx_priority priority,
struct pvr_winsys_compute_ctx_create_info *const create_info)
{
create_info->priority = priority;
pvr_compute_ctx_ws_static_state_init(&ctx->device->pdevice->dev_info,
ctx,
&create_info->static_state);
}
VkResult pvr_compute_ctx_create(struct pvr_device *const device,
enum pvr_winsys_ctx_priority priority,
struct pvr_compute_ctx **const ctx_out)
{
struct pvr_winsys_compute_ctx_create_info create_info;
struct pvr_compute_ctx *ctx;
VkResult result;
ctx = vk_alloc(&device->vk.alloc,
sizeof(*ctx),
8,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!ctx)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
ctx->device = device;
result = pvr_bo_alloc(
device,
device->heaps.general_heap,
rogue_get_cdm_context_resume_buffer_size(&device->pdevice->dev_info),
rogue_get_cdm_context_resume_buffer_alignment(&device->pdevice->dev_info),
PVR_WINSYS_BO_FLAG_CPU_ACCESS | PVR_WINSYS_BO_FLAG_GPU_UNCACHED,
&ctx->ctx_switch.compute_state_bo);
if (result != VK_SUCCESS)
goto err_free_ctx;
/* TODO: Change this so that enabling storage to B doesn't change the array
* size. Instead of looping we could unroll this and have the second
* programs setup depending on the B enable. Doing it that way would make
* things more obvious.
*/
for (uint32_t i = 0; i < ARRAY_SIZE(ctx->ctx_switch.sr); i++) {
result = pvr_ctx_sr_programs_setup(device,
PVR_CTX_SR_COMPUTE_TARGET,
&ctx->ctx_switch.sr[i]);
if (result != VK_SUCCESS) {
for (uint32_t j = 0; j < i; j++)
pvr_ctx_sr_programs_cleanup(device, &ctx->ctx_switch.sr[j]);
goto err_free_state_buffer;
}
}
result = pvr_pds_sr_fence_terminate_program_create_and_upload(
device,
&ctx->ctx_switch.sr_fence_terminate_program);
if (result != VK_SUCCESS)
goto err_free_sr_programs;
pvr_compute_ctx_ws_create_info_init(ctx, priority, &create_info);
result = pvr_ctx_reset_cmd_init(device, &ctx->reset_cmd);
if (result != VK_SUCCESS)
goto err_free_pds_fence_terminate_program;
result = device->ws->ops->compute_ctx_create(device->ws,
&create_info,
&ctx->ws_ctx);
if (result != VK_SUCCESS)
goto err_fini_reset_cmd;
*ctx_out = ctx;
return VK_SUCCESS;
err_fini_reset_cmd:
pvr_ctx_reset_cmd_fini(device, &ctx->reset_cmd);
err_free_pds_fence_terminate_program:
pvr_bo_free(device, ctx->ctx_switch.sr_fence_terminate_program.pvr_bo);
err_free_sr_programs:
for (uint32_t i = 0; i < ARRAY_SIZE(ctx->ctx_switch.sr); ++i)
pvr_ctx_sr_programs_cleanup(device, &ctx->ctx_switch.sr[i]);
err_free_state_buffer:
pvr_bo_free(device, ctx->ctx_switch.compute_state_bo);
err_free_ctx:
vk_free(&device->vk.alloc, ctx);
return result;
}
void pvr_compute_ctx_destroy(struct pvr_compute_ctx *const ctx)
{
struct pvr_device *device = ctx->device;
device->ws->ops->compute_ctx_destroy(ctx->ws_ctx);
pvr_ctx_reset_cmd_fini(device, &ctx->reset_cmd);
pvr_bo_free(device, ctx->ctx_switch.sr_fence_terminate_program.pvr_bo);
for (uint32_t i = 0; i < ARRAY_SIZE(ctx->ctx_switch.sr); ++i)
pvr_ctx_sr_programs_cleanup(device, &ctx->ctx_switch.sr[i]);
pvr_bo_free(device, ctx->ctx_switch.compute_state_bo);
vk_free(&device->vk.alloc, ctx);
}
static void pvr_transfer_ctx_ws_create_info_init(
enum pvr_winsys_ctx_priority priority,
struct pvr_winsys_transfer_ctx_create_info *const create_info)
{
create_info->priority = priority;
}
static VkResult pvr_transfer_ctx_setup_shaders(struct pvr_device *device,
struct pvr_transfer_ctx *ctx)
{
const uint32_t cache_line_size =
rogue_get_slc_cache_line_size(&device->pdevice->dev_info);
VkResult result;
/* TODO: Setup USC fragments. */
/* Setup EOT program. */
result = pvr_gpu_upload_usc(device,
pvr_transfer_eot_usc_code,
sizeof(pvr_transfer_eot_usc_code),
cache_line_size,
&ctx->usc_eot_bo);
if (result != VK_SUCCESS)
return result;
STATIC_ASSERT(ARRAY_SIZE(pvr_transfer_eot_usc_offsets) ==
ARRAY_SIZE(ctx->transfer_mrts));
for (uint32_t i = 0U; i < ARRAY_SIZE(pvr_transfer_eot_usc_offsets); i++) {
ctx->transfer_mrts[i] =
PVR_DEV_ADDR_OFFSET(ctx->usc_eot_bo->vma->dev_addr,
pvr_transfer_eot_usc_offsets[i]);
}
return VK_SUCCESS;
}
static void pvr_transfer_ctx_fini_shaders(struct pvr_device *device,
struct pvr_transfer_ctx *ctx)
{
pvr_bo_free(device, ctx->usc_eot_bo);
}
VkResult pvr_transfer_ctx_create(struct pvr_device *const device,
enum pvr_winsys_ctx_priority priority,
struct pvr_transfer_ctx **const ctx_out)
{
struct pvr_winsys_transfer_ctx_create_info create_info;
struct pvr_transfer_ctx *ctx;
VkResult result;
ctx = vk_zalloc(&device->vk.alloc,
sizeof(*ctx),
8U,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!ctx)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
ctx->device = device;
result = pvr_ctx_reset_cmd_init(device, &ctx->reset_cmd);
if (result != VK_SUCCESS)
goto err_free_ctx;
pvr_transfer_ctx_ws_create_info_init(priority, &create_info);
result = device->ws->ops->transfer_ctx_create(device->ws,
&create_info,
&ctx->ws_ctx);
if (result != VK_SUCCESS)
goto err_fini_reset_cmd;
result = pvr_transfer_ctx_setup_shaders(device, ctx);
if (result != VK_SUCCESS)
goto err_destroy_transfer_ctx;
/* Create the PDS Uniform/Tex state code segment array. */
for (uint32_t i = 0U; i < ARRAY_SIZE(ctx->pds_unitex_code); i++) {
for (uint32_t j = 0U; j < ARRAY_SIZE(ctx->pds_unitex_code[0U]); j++) {
if (i == 0U && j == 0U)
continue;
result = pvr_pds_unitex_state_program_create_and_upload(
device,
NULL,
i,
j,
&ctx->pds_unitex_code[i][j]);
if (result != VK_SUCCESS) {
goto err_free_pds_unitex_bos;
}
}
}
*ctx_out = ctx;
return VK_SUCCESS;
err_free_pds_unitex_bos:
for (uint32_t i = 0U; i < ARRAY_SIZE(ctx->pds_unitex_code); i++) {
for (uint32_t j = 0U; j < ARRAY_SIZE(ctx->pds_unitex_code[0U]); j++) {
if (!ctx->pds_unitex_code[i][j].pvr_bo)
continue;
pvr_bo_free(device, ctx->pds_unitex_code[i][j].pvr_bo);
}
}
pvr_transfer_ctx_fini_shaders(device, ctx);
err_destroy_transfer_ctx:
device->ws->ops->transfer_ctx_destroy(ctx->ws_ctx);
err_fini_reset_cmd:
pvr_ctx_reset_cmd_fini(device, &ctx->reset_cmd);
err_free_ctx:
vk_free(&device->vk.alloc, ctx);
return result;
}
void pvr_transfer_ctx_destroy(struct pvr_transfer_ctx *const ctx)
{
struct pvr_device *device = ctx->device;
for (uint32_t i = 0U; i < ARRAY_SIZE(ctx->pds_unitex_code); i++) {
for (uint32_t j = 0U; j < ARRAY_SIZE(ctx->pds_unitex_code[0U]); j++) {
if (!ctx->pds_unitex_code[i][j].pvr_bo)
continue;
pvr_bo_free(device, ctx->pds_unitex_code[i][j].pvr_bo);
}
}
pvr_transfer_ctx_fini_shaders(device, ctx);
device->ws->ops->transfer_ctx_destroy(ctx->ws_ctx);
pvr_ctx_reset_cmd_fini(device, &ctx->reset_cmd);
vk_free(&device->vk.alloc, ctx);
}