blob: cd193a36fe5057dc1b767f401fc4aafe16f5c34f [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
//
//
#include "render_impl.h"
#include "block_pool.h"
#include "common/vk/assert.h"
#include "common/vk/barrier.h"
#include "composition_impl.h"
#include "device.h"
#include "dispatch.h"
#include "queue_pool.h"
#include "spinel.h"
#include "spinel_assert.h"
#include "spinel_vk_types.h"
#include "styling_impl.h"
#include "vk_target.h"
//
//
//
#include <stdio.h>
#include <string.h>
//
// Used to probe the type
//
struct spn_vk_render_submit_ext_base
{
void * ext;
spn_vk_render_submit_ext_type_e type;
};
//
// A callback is only invoked if a H2D copy is required.
//
struct spn_ri_complete_payload
{
struct spn_device * device;
struct spn_composition * composition;
struct spn_styling * styling;
struct
{
struct spn_vk_ds_ttcks_t ttcks;
struct spn_vk_ds_styling_t styling;
struct spn_vk_ds_surface_t surface;
} ds;
};
//
//
//
static void
spn_ri_complete(void * pfn_payload)
{
struct spn_ri_complete_payload const * const payload = pfn_payload;
struct spn_vk * const instance = payload->device->instance;
// release descriptor sets
spn_vk_ds_release_ttcks(instance, payload->ds.ttcks);
spn_vk_ds_release_styling(instance, payload->ds.styling);
spn_vk_ds_release_surface(instance, payload->ds.surface);
// release locks on composition and styling
spn_composition_post_render(payload->composition);
spn_styling_post_render(payload->styling);
}
//
//
//
static spn_result_t
spn_ri_image_render(struct spn_device * const device, spn_render_submit_t const * const submit)
{
//
// accumulate extensions
//
struct spn_vk_render_submit_ext_image_pre_barrier * pre_barrier = NULL;
struct spn_vk_render_submit_ext_image_pre_clear * pre_clear = NULL;
struct spn_vk_render_submit_ext_image_process * pre_process = NULL;
struct spn_vk_render_submit_ext_image_render * render = NULL;
struct spn_vk_render_submit_ext_image_process * post_process = NULL;
struct spn_vk_render_submit_ext_image_post_copy_to_buffer * post_copy_to_buffer = NULL;
struct spn_vk_render_submit_ext_image_post_copy_to_image * post_copy_to_image = NULL;
struct spn_vk_render_submit_ext_image_post_barrier * post_barrier = NULL;
void * ext_next = submit->ext;
while (ext_next != NULL)
{
struct spn_vk_render_submit_ext_base * const base = ext_next;
switch (base->type)
{
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_PRE_BARRIER:
pre_barrier = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_PRE_CLEAR:
pre_clear = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_PRE_PROCESS:
pre_process = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_RENDER:
render = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_POST_PROCESS:
post_process = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_POST_COPY_TO_BUFFER:
post_copy_to_buffer = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_POST_COPY_TO_IMAGE:
post_copy_to_image = ext_next;
break;
case SPN_VK_RENDER_SUBMIT_EXT_TYPE_IMAGE_POST_BARRIER:
post_barrier = ext_next;
break;
default:
return SPN_ERROR_RENDER_EXTENSION_INVALID;
}
ext_next = base->ext;
}
//
// NOTE(allanmac): The RENDER extension must be in the chain.
//
if (render == NULL)
{
return SPN_ERROR_RENDER_EXTENSION_INVALID;
}
//
// acquire a dispatch
//
spn_dispatch_id_t id;
spn(device_dispatch_acquire(device, SPN_DISPATCH_STAGE_RENDER, &id));
//
// declare that the styling and composition happen before this render
//
spn_composition_happens_before(submit->composition, id);
spn_styling_happens_before(submit->styling, id);
//
// get a cb
//
VkCommandBuffer cb = spn_device_dispatch_get_cb(device, id);
//
// accumulate barrier state
//
// NOTE(allanmac): top-of-pipe and zeroes in the member are exactly
// what we want to start with.
//
// NOTE(allanmac): realize that all memory is visible -- image layout
// transitions and transfers are all we're concerned with.
//
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkImageMemoryBarrier imgbar = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
uint32_t imgbar_count = 0;
//
// set imgbar defaults
//
// imgbar.srcAccessMask is 0
//
imgbar.oldLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = device->environment.qfi;
imgbar.dstQueueFamilyIndex = device->environment.qfi;
imgbar.image = render->image;
//
// NOTE(allanmac): Simplifying assumption below
//
imgbar.subresourceRange = (VkImageSubresourceRange){
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
};
//
// set the submission callback and data
//
spn_device_dispatch_set_submitter(device, id, render->submitter_pfn, render->submitter_data);
//
// the extensions are always processed in this order:
//
// PRE_BARRIER>PRE_CLEAR>RENDER>POST_COPY>POST_BARRIER
//
//
// layout transition or queue family ownership transfer?
//
if (pre_barrier != NULL)
{
//
// imgbar.srcAccessMask -- use default
// imgbar.dstAccessMask -- not set
// imgbar.dstQueueFamilyIndex -- use default
// imgbar.image -- use default
//
uint32_t const src_qfi = (pre_barrier->src_qfi == VK_QUEUE_FAMILY_IGNORED)
? device->environment.qfi
: pre_barrier->src_qfi;
imgbar.oldLayout = pre_barrier->old_layout;
imgbar.newLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = src_qfi;
imgbar.image = render->image;
imgbar_count = 1;
}
//
// clear?
//
if (pre_clear != NULL)
{
//
// imgbar.srcAccessMask -- use default
// imgbar.oldLayout -- use default
// imgbar.srcQueueFamilyIndex -- use default
// imgbar.dstQueueFamilyIndex -- use default
// imgbar.image -- use default
//
imgbar.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imgbar.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imgbar_count = 1;
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
vkCmdClearColorImage(cb,
render->image,
imgbar.newLayout,
pre_clear->color,
1,
&imgbar.subresourceRange);
//
// post command -- transition to render layout
//
src_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
//
// imgbar.dstQueueFamilyIndex -- use default
// imgbar.image -- use default
//
imgbar.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imgbar.oldLayout = imgbar.newLayout;
imgbar.newLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = device->environment.qfi;
// imgbar_count is 1
}
//
// process?
//
if (pre_process != NULL)
{
//
// imgbar.srcAccessMask -- use default
// imgbar.oldLayout -- use default
// imgbar.srcQueueFamilyIndex -- use default
// imgbar.image -- use default
//
imgbar.dstAccessMask = pre_process->access_mask;
imgbar.newLayout = render->image_info.imageLayout;
imgbar.dstQueueFamilyIndex = device->environment.qfi;
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_COMPUTE, pre_process->pipeline);
vkCmdBindDescriptorSets(cb,
VK_PIPELINE_BIND_POINT_COMPUTE,
pre_process->pipeline_layout,
0,
pre_process->descriptor_set_count,
pre_process->descriptor_sets,
0,
0);
vkCmdPushConstants(cb,
pre_process->pipeline_layout,
VK_SHADER_STAGE_COMPUTE_BIT,
pre_process->push_offset,
pre_process->push_size,
pre_process->push_values);
vkCmdDispatch(cb,
pre_process->group_count_x,
pre_process->group_count_y,
pre_process->group_count_z);
//
// post command -- transition to render layout
//
src_stage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
imgbar.srcAccessMask = imgbar.dstAccessMask;
imgbar.oldLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = device->environment.qfi;
imgbar_count = 1;
}
//
// DS: BLOCK POOL
//
struct spn_vk * const instance = device->instance;
spn_vk_ds_bind_render_block_pool(instance, cb, spn_device_block_pool_get_ds(device));
//
// DS: TTCKS
//
struct spn_vk_ds_ttcks_t ds_ttcks;
spn_composition_pre_render_bind_ds(submit->composition, &ds_ttcks, cb);
//
// DS: STYLING
//
struct spn_vk_ds_styling_t ds_styling;
spn_styling_pre_render_bind_ds(submit->styling, &ds_styling, cb);
//
// DS: SURFACE
//
struct spn_vk_ds_surface_t ds_surface;
spn_vk_ds_acquire_surface(instance, device, &ds_surface);
// copy the dbi structs
*spn_vk_ds_get_surface_surface(instance, ds_surface) = render->image_info;
// update ds
spn_vk_ds_update_surface(instance, &device->environment, ds_surface);
// bind ds
spn_vk_ds_bind_render_surface(instance, cb, ds_surface);
//
// append push constants
//
// convert pixel clip coords to tile coords
//
// FIXME(allanmac): use the signed SIMD4 trick
//
// FIXME(allanmac): this is nearly identical to the composition_impl.c clip
//
struct spn_vk_target_config const * const config = spn_vk_get_config(device->instance);
uint32_t const tile_w = 1 << config->tile.width_log2;
uint32_t const tile_h = 1 << config->tile.height_log2;
uint32_t const surf_w_max = tile_w << SPN_TTCK_HI_BITS_X;
uint32_t const surf_h_max = tile_h << SPN_TTCK_HI_BITS_Y;
struct spn_uvec4 const tile_clip = {
submit->clip[0] >> config->tile.width_log2,
submit->clip[1] >> config->tile.height_log2,
(MIN_MACRO(uint32_t, submit->clip[2], surf_w_max) + tile_w - 1) >> config->tile.width_log2,
(MIN_MACRO(uint32_t, submit->clip[3], surf_h_max) + tile_h - 1) >> config->tile.height_log2
};
struct spn_vk_push_render const push = {
.render_clip = { .x = MIN_MACRO(uint32_t, tile_clip.x, 1 << SPN_TTCK_HI_BITS_X),
.y = MIN_MACRO(uint32_t, tile_clip.y, 1 << SPN_TTCK_HI_BITS_Y),
.z = MIN_MACRO(uint32_t, tile_clip.z, 1 << SPN_TTCK_HI_BITS_X),
.w = MIN_MACRO(uint32_t, tile_clip.w, 1 << SPN_TTCK_HI_BITS_Y) },
};
spn_vk_p_push_render(instance, cb, &push);
//
// PIPELINE: RENDER
//
// - indirect dispatch the pipeline
// - shader only *writes* to surface
//
{
imgbar.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
spn_vk_p_bind_render(instance, cb);
spn_composition_pre_render_dispatch_indirect(submit->composition, cb);
//
// post render
//
src_stage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
imgbar.srcAccessMask = imgbar.dstAccessMask;
imgbar.dstAccessMask = 0;
imgbar.oldLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = device->environment.qfi;
imgbar.dstQueueFamilyIndex = device->environment.qfi;
imgbar_count = 0;
}
//
// process?
//
if (post_process != NULL)
{
imgbar.dstAccessMask = post_process->access_mask;
imgbar.newLayout = render->image_info.imageLayout;
imgbar_count = 1;
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_COMPUTE, post_process->pipeline);
vkCmdBindDescriptorSets(cb,
VK_PIPELINE_BIND_POINT_COMPUTE,
post_process->pipeline_layout,
0,
post_process->descriptor_set_count,
post_process->descriptor_sets,
0,
0);
vkCmdPushConstants(cb,
post_process->pipeline_layout,
VK_SHADER_STAGE_COMPUTE_BIT,
post_process->push_offset,
post_process->push_size,
post_process->push_values);
vkCmdDispatch(cb,
post_process->group_count_x,
post_process->group_count_y,
post_process->group_count_z);
imgbar.srcAccessMask = imgbar.dstAccessMask;
imgbar.dstAccessMask = 0;
imgbar_count = 0;
}
//
// copy to buffer?
//
if (post_copy_to_buffer != NULL)
{
//
// imgbar.srcAccessMask -- use default
// imgbar.oldLayout -- use default
// imgbar.srcQueueFamilyIndex -- use default
// imgbar.dstQueueFamilyIndex -- use default
// imgbar.image -- use default
//
imgbar.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imgbar.oldLayout = render->image_info.imageLayout;
imgbar.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imgbar_count = 1;
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
vkCmdCopyImageToBuffer(cb,
render->image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
post_copy_to_buffer->dst,
post_copy_to_buffer->region_count,
post_copy_to_buffer->regions);
//
// post copy -- transition the image back to default
//
src_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
//
// imgbar.dstQueueFamilyIndex -- not set
// imgbar.image -- use default
//
imgbar.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imgbar.dstAccessMask = 0;
imgbar.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imgbar.newLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = device->environment.qfi; // ignored
imgbar_count = 1;
}
//
// copy to image?
//
if (post_copy_to_image != NULL)
{
//
// imgbar.srcAccessMask -- use default
// imgbar.oldLayout -- use default
// imgbar.srcQueueFamilyIndex -- use default
// imgbar.dstQueueFamilyIndex -- use default
// imgbar.image -- use default
//
imgbar.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imgbar.oldLayout = render->image_info.imageLayout;
imgbar.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imgbar_count = 1;
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
vkCmdCopyImage(cb,
render->image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
post_copy_to_image->dst,
post_copy_to_image->dst_layout,
post_copy_to_image->region_count,
post_copy_to_image->regions);
//
// post copy -- transition the image back to default
//
src_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
//
// imgbar.dstQueueFamilyIndex -- not set
// imgbar.image -- use default
//
imgbar.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imgbar.dstAccessMask = 0;
imgbar.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imgbar.newLayout = render->image_info.imageLayout;
imgbar.srcQueueFamilyIndex = device->environment.qfi; // ignored
imgbar_count = 1;
}
//
// layout transition or queue family ownership transfer?
//
if (post_barrier != NULL)
{
//
// imgbar.srcAccessMask -- use default
// imgbar.dstAccessMask -- use default
// imgbar.oldLayout -- use default
// imgbar.srcQueueFamilyIndex -- use default
// imgbar.image -- use default
//
uint32_t const dst_qfi = (post_barrier->dst_qfi == VK_QUEUE_FAMILY_IGNORED)
? device->environment.qfi
: post_barrier->dst_qfi;
imgbar.newLayout = post_barrier->new_layout;
imgbar.dstQueueFamilyIndex = dst_qfi;
imgbar_count = 1;
}
//
// final barrier
//
vkCmdPipelineBarrier(cb,
src_stage,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
NULL,
0,
NULL,
imgbar_count,
&imgbar);
//
// set the completion payload
//
struct spn_ri_complete_payload * const payload =
spn_device_dispatch_set_completion(device, id, spn_ri_complete, sizeof(*payload));
payload->composition = submit->composition;
payload->styling = submit->styling;
payload->device = device;
payload->ds.ttcks = ds_ttcks;
payload->ds.styling = ds_styling;
payload->ds.surface = ds_surface;
//
// submit the dispatch
//
spn_device_dispatch_submit(device, id);
return SPN_SUCCESS;
}
//
//
//
spn_result_t
spn_render_impl(struct spn_device * const device, spn_render_submit_t const * const submit)
{
//
// seal the composition
//
{
spn_result_t const res_c = spn_composition_seal(submit->composition);
if (res_c)
{
return res_c;
}
}
//
// seal the styling
//
{
spn_result_t const res_s = spn_styling_seal(submit->styling);
if (res_s)
{
return res_s;
}
}
//
// walk the extension chain
//
struct spn_vk_render_submit_ext_base const * const ext = submit->ext;
if (ext == NULL)
{
return SPN_ERROR_RENDER_EXTENSION_INVALID;
}
return spn_ri_image_render(device, submit);
}
//
//
//