v3dv: make the driver more robust against OOM
This is generally very difficult to handle properly everywhere, but at least this is good enough to make the few CTS tests for this happy. Fixes (on Rpi4): dEQP-VK.wsi.xlib.swapchain.simulate_oom.* Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
This commit is contained in:
committed by
Marge Bot
parent
57d0ff8d48
commit
10dbb1e07d
@@ -34,8 +34,10 @@ v3dv_bo_alloc(struct v3dv_device *device, uint32_t size, const char *name)
|
||||
{
|
||||
struct v3dv_bo *bo = vk_alloc(&device->alloc, sizeof(struct v3dv_bo), 8,
|
||||
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
|
||||
if (!bo)
|
||||
if (!bo) {
|
||||
fprintf(stderr, "Failed to allocate host memory for BO\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const uint32_t page_align = 4096; /* Always allocate full pages */
|
||||
size = align(size, page_align);
|
||||
@@ -45,6 +47,7 @@ v3dv_bo_alloc(struct v3dv_device *device, uint32_t size, const char *name)
|
||||
|
||||
int ret = v3dv_ioctl(device->render_fd, DRM_IOCTL_V3D_CREATE_BO, &create);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Failed to allocate device memory for BO\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -64,7 +67,8 @@ v3dv_bo_alloc(struct v3dv_device *device, uint32_t size, const char *name)
|
||||
bool
|
||||
v3dv_bo_free(struct v3dv_device *device, struct v3dv_bo *bo)
|
||||
{
|
||||
assert(bo);
|
||||
if (!bo)
|
||||
return true;
|
||||
|
||||
if (bo->map)
|
||||
v3dv_bo_unmap(device, bo);
|
||||
|
||||
@@ -73,16 +73,18 @@ v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment)
|
||||
|
||||
struct v3dv_bo *bo = v3dv_bo_alloc(cl->job->device, space, "CL");
|
||||
if (!bo) {
|
||||
fprintf(stderr, "failed to allocate memory for command list");
|
||||
abort();
|
||||
fprintf(stderr, "failed to allocate memory for command list\n");
|
||||
v3dv_flag_oom(NULL, cl->job);
|
||||
return 0;
|
||||
}
|
||||
|
||||
v3dv_job_add_bo(cl->job, bo);
|
||||
|
||||
bool ok = v3dv_bo_map(cl->job->device, bo, bo->size);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "failed to map command list buffer");
|
||||
abort();
|
||||
fprintf(stderr, "failed to map command list buffer\n");
|
||||
v3dv_flag_oom(NULL, cl->job);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cl->bo = bo;
|
||||
@@ -93,7 +95,6 @@ v3dv_cl_ensure_space(struct v3dv_cl *cl, uint32_t space, uint32_t alignment)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space)
|
||||
{
|
||||
@@ -102,8 +103,9 @@ v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space)
|
||||
|
||||
struct v3dv_bo *bo = v3dv_bo_alloc(cl->job->device, space, "CL");
|
||||
if (!bo) {
|
||||
fprintf(stderr, "failed to allocate memory for command list");
|
||||
abort();
|
||||
fprintf(stderr, "failed to allocate memory for command list\n");
|
||||
v3dv_flag_oom(NULL, cl->job);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Chain to the new BO from the old one if needed */
|
||||
@@ -117,8 +119,9 @@ v3dv_cl_ensure_space_with_branch(struct v3dv_cl *cl, uint32_t space)
|
||||
|
||||
bool ok = v3dv_bo_map(cl->job->device, bo, bo->size);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "failed to map command list buffer");
|
||||
abort();
|
||||
fprintf(stderr, "failed to map command list buffer\n");
|
||||
v3dv_flag_oom(NULL, cl->job);
|
||||
return;
|
||||
}
|
||||
|
||||
cl->bo = bo;
|
||||
|
||||
@@ -206,7 +206,7 @@ v3dv_cmd_buffer_add_private_obj(struct v3dv_cmd_buffer *cmd_buffer,
|
||||
vk_alloc(&cmd_buffer->device->alloc, sizeof(*pobj), 8,
|
||||
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
|
||||
if (!pobj) {
|
||||
cmd_buffer->state.oom = true;
|
||||
v3dv_flag_oom(cmd_buffer, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,10 @@ void
|
||||
v3dv_job_emit_binning_flush(struct v3dv_job *job)
|
||||
{
|
||||
assert(job);
|
||||
|
||||
v3dv_cl_ensure_space_with_branch(&job->bcl, cl_packet_length(FLUSH));
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
cl_emit(&job->bcl, FLUSH, flush);
|
||||
}
|
||||
|
||||
@@ -448,6 +451,7 @@ v3dv_job_start_frame(struct v3dv_job *job,
|
||||
render_target_count, max_internal_bpp);
|
||||
|
||||
v3dv_cl_ensure_space_with_branch(&job->bcl, 256);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
/* The PTB will request the tile alloc initial size per tile at start
|
||||
* of tile binning.
|
||||
@@ -473,6 +477,11 @@ v3dv_job_start_frame(struct v3dv_job *job,
|
||||
|
||||
job->tile_alloc = v3dv_bo_alloc(job->device, tile_alloc_size,
|
||||
"tile_alloc");
|
||||
if (!job->tile_alloc) {
|
||||
v3dv_flag_oom(NULL, job);
|
||||
return;
|
||||
}
|
||||
|
||||
v3dv_job_add_bo(job, job->tile_alloc);
|
||||
|
||||
const uint32_t tsda_per_tile_size = 256;
|
||||
@@ -481,6 +490,11 @@ v3dv_job_start_frame(struct v3dv_job *job,
|
||||
tiling->draw_tiles_y *
|
||||
tsda_per_tile_size;
|
||||
job->tile_state = v3dv_bo_alloc(job->device, tile_state_size, "TSDA");
|
||||
if (!job->tile_state) {
|
||||
v3dv_flag_oom(NULL, job);
|
||||
return;
|
||||
}
|
||||
|
||||
v3dv_job_add_bo(job, job->tile_state);
|
||||
|
||||
/* This must go before the binning mode configuration. It is
|
||||
@@ -568,6 +582,12 @@ v3dv_cmd_buffer_finish_job(struct v3dv_cmd_buffer *cmd_buffer)
|
||||
if (!job)
|
||||
return;
|
||||
|
||||
if (cmd_buffer->state.oom) {
|
||||
v3dv_job_destroy(job);
|
||||
cmd_buffer->state.job = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(v3dv_cl_offset(&job->bcl) != 0);
|
||||
|
||||
/* When we merge multiple subpasses into the same job we must only emit one
|
||||
@@ -602,6 +622,8 @@ v3dv_job_init(struct v3dv_job *job,
|
||||
job->device = device;
|
||||
job->cmd_buffer = cmd_buffer;
|
||||
|
||||
list_inithead(&job->list_link);
|
||||
|
||||
if (type == V3DV_JOB_TYPE_GPU_CL) {
|
||||
job->bos =
|
||||
_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
|
||||
@@ -668,7 +690,7 @@ v3dv_cmd_buffer_start_job(struct v3dv_cmd_buffer *cmd_buffer,
|
||||
|
||||
if (!job) {
|
||||
fprintf(stderr, "Error: failed to allocate CPU memory for job\n");
|
||||
cmd_buffer->state.oom = true;
|
||||
v3dv_flag_oom(cmd_buffer, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1405,6 +1427,8 @@ cmd_buffer_render_pass_emit_per_tile_rcl(struct v3dv_cmd_buffer *cmd_buffer,
|
||||
*/
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -1568,6 +1592,7 @@ cmd_buffer_emit_render_pass_rcl(struct v3dv_cmd_buffer *cmd_buffer)
|
||||
v3dv_cl_ensure_space_with_branch(&job->rcl, 200 +
|
||||
MAX2(fb_layers, 1) * 256 *
|
||||
cl_packet_length(SUPERTILE_COORDINATES));
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
assert(state->subpass_idx < state->pass->subpass_count);
|
||||
const struct v3dv_subpass *subpass =
|
||||
@@ -2576,6 +2601,7 @@ emit_depth_bias(struct v3dv_cmd_buffer *cmd_buffer)
|
||||
assert(job);
|
||||
|
||||
v3dv_cl_ensure_space_with_branch(&job->bcl, cl_packet_length(DEPTH_OFFSET));
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
struct v3dv_dynamic_state *dynamic = &cmd_buffer->state.dynamic;
|
||||
cl_emit(&job->bcl, DEPTH_OFFSET, bias) {
|
||||
@@ -2595,6 +2621,8 @@ emit_line_width(struct v3dv_cmd_buffer *cmd_buffer)
|
||||
assert(job);
|
||||
|
||||
v3dv_cl_ensure_space_with_branch(&job->bcl, cl_packet_length(LINE_WIDTH));
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
cl_emit(&job->bcl, LINE_WIDTH, line) {
|
||||
line.line_width = cmd_buffer->state.dynamic.line_width;
|
||||
}
|
||||
@@ -2618,6 +2646,7 @@ emit_blend(struct v3dv_cmd_buffer *cmd_buffer)
|
||||
cl_packet_length(COLOR_WRITE_MASKS);
|
||||
|
||||
v3dv_cl_ensure_space_with_branch(&job->bcl, blend_packets_size);
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
if (cmd_buffer->state.dirty & V3DV_CMD_DIRTY_PIPELINE) {
|
||||
if (pipeline->blend.enables) {
|
||||
@@ -2810,6 +2839,7 @@ emit_gl_shader_state(struct v3dv_cmd_buffer *cmd_buffer)
|
||||
num_elements_to_emit *
|
||||
cl_packet_length(GL_SHADER_STATE_ATTRIBUTE_RECORD),
|
||||
32);
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
cl_emit_with_prepacked(&job->indirect, GL_SHADER_STATE_RECORD,
|
||||
pipeline->shader_state_record, shader) {
|
||||
|
||||
@@ -768,6 +768,8 @@ emit_tlb_clear_per_tile_rcl(struct v3dv_cmd_buffer *cmd_buffer,
|
||||
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -909,6 +911,7 @@ emit_tlb_clear_job(struct v3dv_cmd_buffer *cmd_buffer,
|
||||
v3dv_cl_ensure_space_with_branch(rcl, 200 +
|
||||
layer_count * 256 *
|
||||
cl_packet_length(SUPERTILE_COORDINATES));
|
||||
v3dv_return_if_oom(cmd_buffer, NULL);
|
||||
|
||||
const struct v3dv_frame_tiling *tiling = &job->frame_tiling;
|
||||
cl_emit(rcl, TILE_RENDERING_MODE_CFG_COMMON, config) {
|
||||
|
||||
@@ -213,6 +213,8 @@ emit_rcl_prologue(struct v3dv_job *job,
|
||||
v3dv_cl_ensure_space_with_branch(rcl, 200 +
|
||||
tiling->layers * 256 *
|
||||
cl_packet_length(SUPERTILE_COORDINATES));
|
||||
if (job->cmd_buffer->state.oom)
|
||||
return NULL;
|
||||
|
||||
cl_emit(rcl, TILE_RENDERING_MODE_CFG_COMMON, config) {
|
||||
config.early_z_disable = true;
|
||||
@@ -294,6 +296,8 @@ emit_frame_setup(struct v3dv_job *job,
|
||||
uint32_t layer,
|
||||
const union v3dv_clear_value *clear_value)
|
||||
{
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
const struct v3dv_frame_tiling *tiling = &job->frame_tiling;
|
||||
|
||||
struct v3dv_cl *rcl = &job->rcl;
|
||||
@@ -343,6 +347,8 @@ static void
|
||||
emit_supertile_coordinates(struct v3dv_job *job,
|
||||
struct framebuffer_data *framebuffer)
|
||||
{
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl *rcl = &job->rcl;
|
||||
|
||||
const uint32_t min_y = framebuffer->min_y_supertile;
|
||||
@@ -550,6 +556,8 @@ emit_copy_layer_to_buffer_per_tile_list(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -633,6 +641,8 @@ emit_copy_image_to_buffer_rcl(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *rcl =
|
||||
emit_rcl_prologue(job, framebuffer->internal_type, NULL);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
for (int layer = 0; layer < job->frame_tiling.layers; layer++)
|
||||
emit_copy_layer_to_buffer(job, buffer, image, framebuffer, layer, region);
|
||||
cl_emit(rcl, END_OF_RENDERING, end);
|
||||
@@ -1056,6 +1066,8 @@ emit_copy_image_layer_per_tile_list(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -1112,6 +1124,8 @@ emit_copy_image_rcl(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *rcl =
|
||||
emit_rcl_prologue(job, framebuffer->internal_type, NULL);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
for (int layer = 0; layer < job->frame_tiling.layers; layer++)
|
||||
emit_copy_image_layer(job, dst, src, framebuffer, layer, region);
|
||||
cl_emit(rcl, END_OF_RENDERING, end);
|
||||
@@ -1332,6 +1346,8 @@ emit_clear_image_per_tile_list(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -1383,6 +1399,8 @@ emit_clear_image_rcl(struct v3dv_job *job,
|
||||
|
||||
struct v3dv_cl *rcl =
|
||||
emit_rcl_prologue(job, framebuffer->internal_type, &clear_info);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
emit_frame_setup(job, 0, clear_value);
|
||||
emit_clear_image(job, image, framebuffer, aspects, layer, level);
|
||||
cl_emit(rcl, END_OF_RENDERING, end);
|
||||
@@ -1556,6 +1574,8 @@ emit_copy_buffer_per_tile_list(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -1606,6 +1626,8 @@ emit_copy_buffer_rcl(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *rcl =
|
||||
emit_rcl_prologue(job, framebuffer->internal_type, NULL);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
emit_frame_setup(job, 0, NULL);
|
||||
emit_copy_buffer(job, dst, src, dst_offset, src_offset, framebuffer, format);
|
||||
cl_emit(rcl, END_OF_RENDERING, end);
|
||||
@@ -1787,6 +1809,8 @@ emit_fill_buffer_per_tile_list(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -1840,6 +1864,8 @@ emit_fill_buffer_rcl(struct v3dv_job *job,
|
||||
|
||||
struct v3dv_cl *rcl =
|
||||
emit_rcl_prologue(job, framebuffer->internal_type, &clear_info);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
emit_frame_setup(job, 0, &clear_value);
|
||||
emit_fill_buffer(job, bo, offset, framebuffer);
|
||||
cl_emit(rcl, END_OF_RENDERING, end);
|
||||
@@ -1921,6 +1947,8 @@ emit_copy_buffer_to_layer_per_tile_list(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *cl = &job->indirect;
|
||||
v3dv_cl_ensure_space(cl, 200, 1);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
struct v3dv_cl_reloc tile_list_start = v3dv_cl_get_address(cl);
|
||||
|
||||
cl_emit(cl, TILE_COORDINATES_IMPLICIT, coords);
|
||||
@@ -2037,6 +2065,8 @@ emit_copy_buffer_to_image_rcl(struct v3dv_job *job,
|
||||
{
|
||||
struct v3dv_cl *rcl =
|
||||
emit_rcl_prologue(job, framebuffer->internal_type, NULL);
|
||||
v3dv_return_if_oom(NULL, job);
|
||||
|
||||
for (int layer = 0; layer < job->frame_tiling.layers; layer++)
|
||||
emit_copy_buffer_to_layer(job, image, buffer, framebuffer, layer, region);
|
||||
cl_emit(rcl, END_OF_RENDERING, end);
|
||||
|
||||
@@ -1536,4 +1536,29 @@ v3dv_ioctl(int fd, unsigned long request, void *arg)
|
||||
return drmIoctl(fd, request, arg);
|
||||
}
|
||||
|
||||
/* Flags OOM conditions in command buffer state.
|
||||
*
|
||||
* Note: notice that no-op jobs don't have a command buffer reference.
|
||||
*/
|
||||
static inline void
|
||||
v3dv_flag_oom(struct v3dv_cmd_buffer *cmd_buffer, struct v3dv_job *job)
|
||||
{
|
||||
if (cmd_buffer) {
|
||||
cmd_buffer->state.oom = true;
|
||||
} else {
|
||||
assert(job);
|
||||
if (job->cmd_buffer)
|
||||
job->cmd_buffer->state.oom = true;
|
||||
}
|
||||
}
|
||||
|
||||
#define v3dv_return_if_oom(_cmd_buffer, _job) do { \
|
||||
const struct v3dv_cmd_buffer *__cmd_buffer = _cmd_buffer; \
|
||||
if (__cmd_buffer && __cmd_buffer->state.oom) \
|
||||
return; \
|
||||
const struct v3dv_job *__job = _job; \
|
||||
if (__job && __job->cmd_buffer && __job->cmd_buffer->state.oom) \
|
||||
return; \
|
||||
} while(0) \
|
||||
|
||||
#endif /* V3DV_PRIVATE_H */
|
||||
|
||||
Reference in New Issue
Block a user