mesa/src/panfrost/vulkan/panvk_cmd_buffer.c
Alyssa Rosenzweig 22538b89b3 panfrost: Handle non-dithered clear colours
In b9c095cc2c ("panfrost: Rewrite the clear colour packing code"),
packing of clear colours was corrected to use the tilebuffer's
fractional bits, fixing dithering of the clear colour with formats like
RGB565. Unfortunately, that commit did so unconditionally. If the
framebuffer is dithered, but dithering is disabled at the time of
the clear, we would incorrectly dither the clear.

This is a regression, as the old (broken) code passed the relevant CTS
test. What's the catch? Depending on dither state, there are two
formulas to pack tilebuffer colours. We need to handle both. Fixes
KHR-GLES31.core.draw_buffers_indexed.color_masks.

Fixes: b9c095cc2c ("panfrost: Rewrite the clear colour packing code")
Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12460>
2021-08-18 23:32:40 +00:00

1547 lines
51 KiB
C

/*
* Copyright © 2021 Collabora Ltd.
*
* Derived from tu_cmd_buffer.c which is:
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
* Copyright © 2015 Intel Corporation
*
* 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 "panvk_cs.h"
#include "panvk_private.h"
#include "panfrost-quirks.h"
#include "pan_blitter.h"
#include "pan_encoder.h"
#include "util/rounding.h"
#include "vk_format.h"
static VkResult
panvk_reset_cmdbuf(struct panvk_cmd_buffer *cmdbuf)
{
struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
cmdbuf->record_result = VK_SUCCESS;
list_for_each_entry_safe(struct panvk_batch, batch, &cmdbuf->batches, node) {
list_del(&batch->node);
util_dynarray_fini(&batch->jobs);
if (!pan_is_bifrost(pdev))
panfrost_bo_unreference(batch->tiler.ctx.midgard.polygon_list);
util_dynarray_fini(&batch->event_ops);
vk_free(&cmdbuf->pool->alloc, batch);
}
panvk_pool_reset(&cmdbuf->desc_pool);
panvk_pool_reset(&cmdbuf->tls_pool);
panvk_pool_reset(&cmdbuf->varying_pool);
cmdbuf->status = PANVK_CMD_BUFFER_STATUS_INITIAL;
for (unsigned i = 0; i < MAX_BIND_POINTS; i++)
memset(&cmdbuf->descriptors[i].sets, 0, sizeof(cmdbuf->descriptors[i].sets));
return cmdbuf->record_result;
}
static VkResult
panvk_create_cmdbuf(struct panvk_device *device,
struct panvk_cmd_pool *pool,
VkCommandBufferLevel level,
struct panvk_cmd_buffer **cmdbuf_out)
{
struct panvk_cmd_buffer *cmdbuf;
cmdbuf = vk_object_zalloc(&device->vk, NULL, sizeof(*cmdbuf),
VK_OBJECT_TYPE_COMMAND_BUFFER);
if (!cmdbuf)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
cmdbuf->device = device;
cmdbuf->level = level;
cmdbuf->pool = pool;
if (pool) {
list_addtail(&cmdbuf->pool_link, &pool->active_cmd_buffers);
cmdbuf->queue_family_index = pool->queue_family_index;
} else {
/* Init the pool_link so we can safely call list_del when we destroy
* the command buffer
*/
list_inithead(&cmdbuf->pool_link);
cmdbuf->queue_family_index = PANVK_QUEUE_GENERAL;
}
panvk_pool_init(&cmdbuf->desc_pool, &device->physical_device->pdev,
pool ? &pool->desc_bo_pool : NULL, 0, 64 * 1024,
"Command buffer descriptor pool", true);
panvk_pool_init(&cmdbuf->tls_pool, &device->physical_device->pdev,
pool ? &pool->tls_bo_pool : NULL,
PAN_BO_INVISIBLE, 64 * 1024, "TLS pool", false);
panvk_pool_init(&cmdbuf->varying_pool, &device->physical_device->pdev,
pool ? &pool->varying_bo_pool : NULL,
PAN_BO_INVISIBLE, 64 * 1024, "Varyings pool", false);
list_inithead(&cmdbuf->batches);
cmdbuf->status = PANVK_CMD_BUFFER_STATUS_INITIAL;
*cmdbuf_out = cmdbuf;
return VK_SUCCESS;
}
static void
panvk_destroy_cmdbuf(struct panvk_cmd_buffer *cmdbuf)
{
struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
struct panvk_device *device = cmdbuf->device;
list_del(&cmdbuf->pool_link);
list_for_each_entry_safe(struct panvk_batch, batch, &cmdbuf->batches, node) {
list_del(&batch->node);
util_dynarray_fini(&batch->jobs);
if (!pan_is_bifrost(pdev))
panfrost_bo_unreference(batch->tiler.ctx.midgard.polygon_list);
util_dynarray_fini(&batch->event_ops);
vk_free(&cmdbuf->pool->alloc, batch);
}
panvk_pool_cleanup(&cmdbuf->desc_pool);
panvk_pool_cleanup(&cmdbuf->tls_pool);
panvk_pool_cleanup(&cmdbuf->varying_pool);
vk_object_free(&device->vk, NULL, cmdbuf);
}
VkResult
panvk_AllocateCommandBuffers(VkDevice _device,
const VkCommandBufferAllocateInfo *pAllocateInfo,
VkCommandBuffer *pCommandBuffers)
{
VK_FROM_HANDLE(panvk_device, device, _device);
VK_FROM_HANDLE(panvk_cmd_pool, pool, pAllocateInfo->commandPool);
VkResult result = VK_SUCCESS;
unsigned i;
for (i = 0; i < pAllocateInfo->commandBufferCount; i++) {
struct panvk_cmd_buffer *cmdbuf = NULL;
if (!list_is_empty(&pool->free_cmd_buffers)) {
cmdbuf = list_first_entry(
&pool->free_cmd_buffers, struct panvk_cmd_buffer, pool_link);
list_del(&cmdbuf->pool_link);
list_addtail(&cmdbuf->pool_link, &pool->active_cmd_buffers);
cmdbuf->level = pAllocateInfo->level;
vk_object_base_reset(&cmdbuf->base);
} else {
result = panvk_create_cmdbuf(device, pool, pAllocateInfo->level, &cmdbuf);
}
if (result != VK_SUCCESS)
goto err_free_cmd_bufs;
pCommandBuffers[i] = panvk_cmd_buffer_to_handle(cmdbuf);
}
return VK_SUCCESS;
err_free_cmd_bufs:
panvk_FreeCommandBuffers(_device, pAllocateInfo->commandPool, i,
pCommandBuffers);
for (unsigned j = 0; j < i; j++)
pCommandBuffers[j] = VK_NULL_HANDLE;
return result;
}
void
panvk_FreeCommandBuffers(VkDevice device,
VkCommandPool commandPool,
uint32_t commandBufferCount,
const VkCommandBuffer *pCommandBuffers)
{
for (uint32_t i = 0; i < commandBufferCount; i++) {
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, pCommandBuffers[i]);
if (cmdbuf) {
if (cmdbuf->pool) {
list_del(&cmdbuf->pool_link);
panvk_reset_cmdbuf(cmdbuf);
list_addtail(&cmdbuf->pool_link,
&cmdbuf->pool->free_cmd_buffers);
} else
panvk_destroy_cmdbuf(cmdbuf);
}
}
}
VkResult
panvk_ResetCommandBuffer(VkCommandBuffer commandBuffer,
VkCommandBufferResetFlags flags)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
return panvk_reset_cmdbuf(cmdbuf);
}
VkResult
panvk_BeginCommandBuffer(VkCommandBuffer commandBuffer,
const VkCommandBufferBeginInfo *pBeginInfo)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
VkResult result = VK_SUCCESS;
if (cmdbuf->status != PANVK_CMD_BUFFER_STATUS_INITIAL) {
/* If the command buffer has already been reset with
* vkResetCommandBuffer, no need to do it again.
*/
result = panvk_reset_cmdbuf(cmdbuf);
if (result != VK_SUCCESS)
return result;
}
memset(&cmdbuf->state, 0, sizeof(cmdbuf->state));
cmdbuf->status = PANVK_CMD_BUFFER_STATUS_RECORDING;
return VK_SUCCESS;
}
void
panvk_CmdBindVertexBuffers(VkCommandBuffer commandBuffer,
uint32_t firstBinding,
uint32_t bindingCount,
const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
assert(firstBinding + bindingCount <= MAX_VBS);
for (uint32_t i = 0; i < bindingCount; i++) {
struct panvk_buffer *buf = panvk_buffer_from_handle(pBuffers[i]);
cmdbuf->state.vb.bufs[firstBinding + i].address = buf->bo->ptr.gpu + pOffsets[i];
cmdbuf->state.vb.bufs[firstBinding + i].size = buf->size - pOffsets[i];
}
cmdbuf->state.vb.count = MAX2(cmdbuf->state.vb.count, firstBinding + bindingCount);
cmdbuf->state.vb.attrib_bufs = cmdbuf->state.vb.attribs = 0;
}
void
panvk_CmdBindIndexBuffer(VkCommandBuffer commandBuffer,
VkBuffer buffer,
VkDeviceSize offset,
VkIndexType indexType)
{
panvk_stub();
}
void
panvk_CmdBindDescriptorSets(VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipelineLayout _layout,
uint32_t firstSet,
uint32_t descriptorSetCount,
const VkDescriptorSet *pDescriptorSets,
uint32_t dynamicOffsetCount,
const uint32_t *pDynamicOffsets)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
VK_FROM_HANDLE(panvk_pipeline_layout, layout, _layout);
struct panvk_descriptor_state *descriptors_state =
&cmdbuf->descriptors[pipelineBindPoint];
for (unsigned i = 0; i < descriptorSetCount; ++i) {
unsigned idx = i + firstSet;
VK_FROM_HANDLE(panvk_descriptor_set, set, pDescriptorSets[i]);
descriptors_state->sets[idx].set = set;
if (layout->num_dynoffsets) {
assert(dynamicOffsetCount >= set->layout->num_dynoffsets);
descriptors_state->sets[idx].dynoffsets =
pan_pool_alloc_aligned(&cmdbuf->desc_pool.base,
ALIGN(layout->num_dynoffsets, 4) *
sizeof(*pDynamicOffsets),
16);
memcpy(descriptors_state->sets[idx].dynoffsets.cpu,
pDynamicOffsets,
sizeof(*pDynamicOffsets) * set->layout->num_dynoffsets);
dynamicOffsetCount -= set->layout->num_dynoffsets;
pDynamicOffsets += set->layout->num_dynoffsets;
}
if (set->layout->num_ubos || set->layout->num_dynoffsets)
descriptors_state->ubos = 0;
if (set->layout->num_textures)
descriptors_state->textures = 0;
if (set->layout->num_samplers)
descriptors_state->samplers = 0;
}
assert(!dynamicOffsetCount);
}
void
panvk_CmdPushConstants(VkCommandBuffer commandBuffer,
VkPipelineLayout layout,
VkShaderStageFlags stageFlags,
uint32_t offset,
uint32_t size,
const void *pValues)
{
panvk_stub();
}
VkResult
panvk_EndCommandBuffer(VkCommandBuffer commandBuffer)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
if (cmdbuf->state.batch)
panvk_cmd_close_batch(cmdbuf);
cmdbuf->status = PANVK_CMD_BUFFER_STATUS_EXECUTABLE;
return cmdbuf->record_result;
}
void
panvk_CmdBindPipeline(VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipeline _pipeline)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
VK_FROM_HANDLE(panvk_pipeline, pipeline, _pipeline);
cmdbuf->state.bind_point = pipelineBindPoint;
cmdbuf->state.pipeline = pipeline;
cmdbuf->state.varyings = pipeline->varyings;
cmdbuf->state.vb.attrib_bufs = cmdbuf->state.vb.attribs = 0;
cmdbuf->state.fs_rsd = 0;
memset(cmdbuf->descriptors[pipelineBindPoint].sysvals, 0,
sizeof(cmdbuf->descriptors[pipelineBindPoint].sysvals));
/* Sysvals are passed through UBOs, we need dirty the UBO array if the
* pipeline contain shaders using sysvals.
*/
if (pipeline->num_sysvals)
cmdbuf->descriptors[pipelineBindPoint].ubos = 0;
}
void
panvk_CmdSetViewport(VkCommandBuffer commandBuffer,
uint32_t firstViewport,
uint32_t viewportCount,
const VkViewport *pViewports)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
assert(viewportCount == 1);
assert(!firstViewport);
cmdbuf->state.viewport = pViewports[0];
cmdbuf->state.vpd = 0;
cmdbuf->state.dirty |= PANVK_DYNAMIC_VIEWPORT;
}
void
panvk_CmdSetScissor(VkCommandBuffer commandBuffer,
uint32_t firstScissor,
uint32_t scissorCount,
const VkRect2D *pScissors)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
assert(scissorCount == 1);
assert(!firstScissor);
cmdbuf->state.scissor = pScissors[0];
cmdbuf->state.vpd = 0;
cmdbuf->state.dirty |= PANVK_DYNAMIC_SCISSOR;
}
void
panvk_CmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
cmdbuf->state.rast.line_width = lineWidth;
cmdbuf->state.dirty |= PANVK_DYNAMIC_LINE_WIDTH;
}
void
panvk_CmdSetDepthBias(VkCommandBuffer commandBuffer,
float depthBiasConstantFactor,
float depthBiasClamp,
float depthBiasSlopeFactor)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
cmdbuf->state.rast.depth_bias.constant_factor = depthBiasConstantFactor;
cmdbuf->state.rast.depth_bias.clamp = depthBiasClamp;
cmdbuf->state.rast.depth_bias.slope_factor = depthBiasSlopeFactor;
cmdbuf->state.dirty |= PANVK_DYNAMIC_DEPTH_BIAS;
cmdbuf->state.fs_rsd = 0;
}
void
panvk_CmdSetBlendConstants(VkCommandBuffer commandBuffer,
const float blendConstants[4])
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
memcpy(cmdbuf->state.blend.constants, blendConstants,
sizeof(cmdbuf->state.blend.constants));
cmdbuf->state.dirty |= PANVK_DYNAMIC_BLEND_CONSTANTS;
cmdbuf->state.fs_rsd = 0;
}
void
panvk_CmdSetDepthBounds(VkCommandBuffer commandBuffer,
float minDepthBounds,
float maxDepthBounds)
{
panvk_stub();
}
void
panvk_CmdSetStencilCompareMask(VkCommandBuffer commandBuffer,
VkStencilFaceFlags faceMask,
uint32_t compareMask)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
if (faceMask & VK_STENCIL_FACE_FRONT_BIT)
cmdbuf->state.zs.s_front.compare_mask = compareMask;
if (faceMask & VK_STENCIL_FACE_BACK_BIT)
cmdbuf->state.zs.s_back.compare_mask = compareMask;
cmdbuf->state.dirty |= PANVK_DYNAMIC_STENCIL_COMPARE_MASK;
cmdbuf->state.fs_rsd = 0;
}
void
panvk_CmdSetStencilWriteMask(VkCommandBuffer commandBuffer,
VkStencilFaceFlags faceMask,
uint32_t writeMask)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
if (faceMask & VK_STENCIL_FACE_FRONT_BIT)
cmdbuf->state.zs.s_front.write_mask = writeMask;
if (faceMask & VK_STENCIL_FACE_BACK_BIT)
cmdbuf->state.zs.s_back.write_mask = writeMask;
cmdbuf->state.dirty |= PANVK_DYNAMIC_STENCIL_WRITE_MASK;
cmdbuf->state.fs_rsd = 0;
}
void
panvk_CmdSetStencilReference(VkCommandBuffer commandBuffer,
VkStencilFaceFlags faceMask,
uint32_t reference)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
if (faceMask & VK_STENCIL_FACE_FRONT_BIT)
cmdbuf->state.zs.s_front.ref = reference;
if (faceMask & VK_STENCIL_FACE_BACK_BIT)
cmdbuf->state.zs.s_back.ref = reference;
cmdbuf->state.dirty |= PANVK_DYNAMIC_STENCIL_REFERENCE;
cmdbuf->state.fs_rsd = 0;
}
void
panvk_CmdExecuteCommands(VkCommandBuffer commandBuffer,
uint32_t commandBufferCount,
const VkCommandBuffer *pCmdBuffers)
{
panvk_stub();
}
VkResult
panvk_CreateCommandPool(VkDevice _device,
const VkCommandPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkCommandPool *pCmdPool)
{
VK_FROM_HANDLE(panvk_device, device, _device);
struct panvk_cmd_pool *pool;
pool = vk_object_alloc(&device->vk, pAllocator, sizeof(*pool),
VK_OBJECT_TYPE_COMMAND_POOL);
if (pool == NULL)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
if (pAllocator)
pool->alloc = *pAllocator;
else
pool->alloc = device->vk.alloc;
list_inithead(&pool->active_cmd_buffers);
list_inithead(&pool->free_cmd_buffers);
pool->queue_family_index = pCreateInfo->queueFamilyIndex;
panvk_bo_pool_init(&pool->desc_bo_pool);
panvk_bo_pool_init(&pool->varying_bo_pool);
panvk_bo_pool_init(&pool->tls_bo_pool);
*pCmdPool = panvk_cmd_pool_to_handle(pool);
return VK_SUCCESS;
}
void
panvk_DestroyCommandPool(VkDevice _device,
VkCommandPool commandPool,
const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(panvk_device, device, _device);
VK_FROM_HANDLE(panvk_cmd_pool, pool, commandPool);
list_for_each_entry_safe(struct panvk_cmd_buffer, cmdbuf,
&pool->active_cmd_buffers, pool_link)
panvk_destroy_cmdbuf(cmdbuf);
list_for_each_entry_safe(struct panvk_cmd_buffer, cmdbuf,
&pool->free_cmd_buffers, pool_link)
panvk_destroy_cmdbuf(cmdbuf);
panvk_bo_pool_cleanup(&pool->desc_bo_pool);
panvk_bo_pool_cleanup(&pool->varying_bo_pool);
panvk_bo_pool_cleanup(&pool->tls_bo_pool);
vk_object_free(&device->vk, pAllocator, pool);
}
VkResult
panvk_ResetCommandPool(VkDevice device,
VkCommandPool commandPool,
VkCommandPoolResetFlags flags)
{
VK_FROM_HANDLE(panvk_cmd_pool, pool, commandPool);
VkResult result;
list_for_each_entry(struct panvk_cmd_buffer, cmdbuf, &pool->active_cmd_buffers,
pool_link)
{
result = panvk_reset_cmdbuf(cmdbuf);
if (result != VK_SUCCESS)
return result;
}
return VK_SUCCESS;
}
void
panvk_TrimCommandPool(VkDevice device,
VkCommandPool commandPool,
VkCommandPoolTrimFlags flags)
{
VK_FROM_HANDLE(panvk_cmd_pool, pool, commandPool);
if (!pool)
return;
list_for_each_entry_safe(struct panvk_cmd_buffer, cmdbuf,
&pool->free_cmd_buffers, pool_link)
panvk_destroy_cmdbuf(cmdbuf);
}
static void
panvk_cmd_prepare_clear_values(struct panvk_cmd_buffer *cmdbuf,
const VkClearValue *in)
{
for (unsigned i = 0; i < cmdbuf->state.pass->attachment_count; i++) {
const struct panvk_render_pass_attachment *attachment =
&cmdbuf->state.pass->attachments[i];
enum pipe_format fmt = attachment->format;
if (util_format_is_depth_or_stencil(fmt)) {
if (attachment->load_op == VK_ATTACHMENT_LOAD_OP_CLEAR ||
attachment->stencil_load_op == VK_ATTACHMENT_LOAD_OP_CLEAR) {
cmdbuf->state.clear[i].depth = in[i].depthStencil.depth;
cmdbuf->state.clear[i].stencil = in[i].depthStencil.stencil;
}
} else if (attachment->load_op == VK_ATTACHMENT_LOAD_OP_CLEAR) {
union pipe_color_union *col = (union pipe_color_union *) &in[i].color;
pan_pack_color(cmdbuf->state.clear[i].color, col, fmt, false);
}
}
}
void
panvk_CmdBeginRenderPass2(VkCommandBuffer commandBuffer,
const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassBeginInfo *pSubpassBeginInfo)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
VK_FROM_HANDLE(panvk_render_pass, pass, pRenderPassBegin->renderPass);
VK_FROM_HANDLE(panvk_framebuffer, fb, pRenderPassBegin->framebuffer);
cmdbuf->state.pass = pass;
cmdbuf->state.subpass = pass->subpasses;
cmdbuf->state.framebuffer = fb;
cmdbuf->state.render_area = pRenderPassBegin->renderArea;
cmdbuf->state.batch = vk_zalloc(&cmdbuf->pool->alloc,
sizeof(*cmdbuf->state.batch), 8,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
util_dynarray_init(&cmdbuf->state.batch->jobs, NULL);
util_dynarray_init(&cmdbuf->state.batch->event_ops, NULL);
cmdbuf->state.clear = vk_zalloc(&cmdbuf->pool->alloc,
sizeof(*cmdbuf->state.clear) *
pRenderPassBegin->clearValueCount, 8,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
assert(pRenderPassBegin->clearValueCount == pass->attachment_count);
panvk_cmd_prepare_clear_values(cmdbuf, pRenderPassBegin->pClearValues);
memset(&cmdbuf->state.compute, 0, sizeof(cmdbuf->state.compute));
}
void
panvk_CmdBeginRenderPass(VkCommandBuffer cmd,
const VkRenderPassBeginInfo *info,
VkSubpassContents contents)
{
VkSubpassBeginInfo subpass_info = {
.sType = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO,
.contents = contents
};
return panvk_CmdBeginRenderPass2(cmd, info, &subpass_info);
}
static void
panvk_cmd_prepare_fragment_job(struct panvk_cmd_buffer *cmdbuf)
{
assert(cmdbuf->state.bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS);
struct panvk_batch *batch = cmdbuf->state.batch;
struct panfrost_ptr job_ptr =
pan_pool_alloc_desc(&cmdbuf->desc_pool.base, FRAGMENT_JOB);
panvk_emit_fragment_job(cmdbuf->device, cmdbuf->state.framebuffer,
cmdbuf->state.batch->fb.desc.gpu,
job_ptr.cpu);
cmdbuf->state.batch->fragment_job = job_ptr.gpu;
util_dynarray_append(&batch->jobs, void *, job_ptr.cpu);
}
void
panvk_cmd_get_midgard_polygon_list(struct panvk_cmd_buffer *cmdbuf,
unsigned width, unsigned height,
bool has_draws)
{
struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
struct panvk_batch *batch = cmdbuf->state.batch;
assert(!pan_is_bifrost(pdev));
if (batch->tiler.ctx.midgard.polygon_list)
return;
unsigned size =
panfrost_tiler_get_polygon_list_size(pdev, width, height, has_draws);
size = util_next_power_of_two(size);
/* Create the BO as invisible if we can. In the non-hierarchical tiler case,
* we need to write the polygon list manually because there's not WRITE_VALUE
* job in the chain. */
bool init_polygon_list = !has_draws && (pdev->quirks & MIDGARD_NO_HIER_TILING);
batch->tiler.ctx.midgard.polygon_list =
panfrost_bo_create(pdev, size,
init_polygon_list ? 0 : PAN_BO_INVISIBLE,
"Polygon list");
if (init_polygon_list) {
assert(batch->tiler.ctx.midgard.polygon_list->ptr.cpu);
uint32_t *polygon_list_body =
batch->tiler.ctx.midgard.polygon_list->ptr.cpu +
MALI_MIDGARD_TILER_MINIMUM_HEADER_SIZE;
polygon_list_body[0] = 0xa0000000;
}
batch->tiler.ctx.midgard.disable = !has_draws;
}
void
panvk_cmd_close_batch(struct panvk_cmd_buffer *cmdbuf)
{
struct panvk_batch *batch = cmdbuf->state.batch;
assert(batch);
if (!batch->fragment_job && !batch->scoreboard.first_job) {
if (util_dynarray_num_elements(&batch->event_ops, struct panvk_event_op) == 0) {
/* Content-less batch, let's drop it */
vk_free(&cmdbuf->pool->alloc, batch);
} else {
/* Batch has no jobs but is needed for synchronization, let's add a
* NULL job so the SUBMIT ioctl doesn't choke on it.
*/
struct panfrost_ptr ptr = pan_pool_alloc_desc(&cmdbuf->desc_pool.base,
JOB_HEADER);
util_dynarray_append(&batch->jobs, void *, ptr.cpu);
panfrost_add_job(&cmdbuf->desc_pool.base, &batch->scoreboard,
MALI_JOB_TYPE_NULL, false, false, 0, 0,
&ptr, false);
list_addtail(&batch->node, &cmdbuf->batches);
}
cmdbuf->state.batch = NULL;
return;
}
struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
list_addtail(&cmdbuf->state.batch->node, &cmdbuf->batches);
struct pan_tls_info tlsinfo = {};
if (cmdbuf->state.pipeline) {
tlsinfo.tls.size = cmdbuf->state.pipeline->tls_size;
tlsinfo.wls.size = cmdbuf->state.pipeline->wls_size;
}
if (tlsinfo.tls.size) {
tlsinfo.tls.ptr =
pan_pool_alloc_aligned(&cmdbuf->tls_pool.base, tlsinfo.tls.size, 4096).gpu;
}
if (tlsinfo.wls.size) {
unsigned wls_size =
pan_wls_mem_size(pdev, &cmdbuf->state.compute.wg_count, tlsinfo.wls.size);
tlsinfo.wls.ptr =
pan_pool_alloc_aligned(&cmdbuf->tls_pool.base, wls_size, 4096).gpu;
}
if ((pan_is_bifrost(pdev) || !cmdbuf->state.batch->fb.desc.cpu) &&
cmdbuf->state.batch->tls.cpu) {
pan_emit_tls(pdev, &tlsinfo, cmdbuf->state.batch->tls.cpu);
}
if (cmdbuf->state.batch->fb.desc.cpu) {
if (!pan_is_bifrost(pdev)) {
panvk_cmd_get_midgard_polygon_list(cmdbuf,
batch->fb.info->width,
batch->fb.info->height,
false);
mali_ptr polygon_list =
cmdbuf->state.batch->tiler.ctx.midgard.polygon_list->ptr.gpu;
struct panfrost_ptr writeval_job =
panfrost_scoreboard_initialize_tiler(&cmdbuf->desc_pool.base,
&cmdbuf->state.batch->scoreboard,
polygon_list);
if (writeval_job.cpu)
util_dynarray_append(&cmdbuf->state.batch->jobs, void *, writeval_job.cpu);
}
cmdbuf->state.batch->fb.desc.gpu |=
panvk_emit_fb(cmdbuf->device,
cmdbuf->state.batch,
cmdbuf->state.subpass,
cmdbuf->state.framebuffer,
cmdbuf->state.clear,
&tlsinfo, &cmdbuf->state.batch->tiler.ctx,
cmdbuf->state.batch->fb.desc.cpu);
if (!pan_is_bifrost(pdev)) {
memcpy(&cmdbuf->state.batch->tiler.templ.midgard,
pan_section_ptr(cmdbuf->state.batch->fb.desc.cpu,
MULTI_TARGET_FRAMEBUFFER, TILER),
sizeof(cmdbuf->state.batch->tiler.templ.midgard));
}
panvk_cmd_prepare_fragment_job(cmdbuf);
}
cmdbuf->state.batch = NULL;
}
void
panvk_cmd_open_batch(struct panvk_cmd_buffer *cmdbuf)
{
assert(!cmdbuf->state.batch);
cmdbuf->state.batch = vk_zalloc(&cmdbuf->pool->alloc,
sizeof(*cmdbuf->state.batch), 8,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
assert(cmdbuf->state.batch);
}
void
panvk_CmdNextSubpass2(VkCommandBuffer commandBuffer,
const VkSubpassBeginInfo *pSubpassBeginInfo,
const VkSubpassEndInfo *pSubpassEndInfo)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
panvk_cmd_close_batch(cmdbuf);
cmdbuf->state.subpass++;
panvk_cmd_open_batch(cmdbuf);
memset(&cmdbuf->state.compute, 0, sizeof(cmdbuf->state.compute));
}
void
panvk_CmdNextSubpass(VkCommandBuffer cmd, VkSubpassContents contents)
{
VkSubpassBeginInfo binfo = {
.sType = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO,
.contents = contents
};
VkSubpassEndInfo einfo = {
.sType = VK_STRUCTURE_TYPE_SUBPASS_END_INFO,
};
panvk_CmdNextSubpass2(cmd, &binfo, &einfo);
}
void
panvk_cmd_alloc_fb_desc(struct panvk_cmd_buffer *cmdbuf)
{
struct panvk_batch *batch = cmdbuf->state.batch;
if (batch->fb.desc.gpu)
return;
const struct panvk_subpass *subpass = cmdbuf->state.subpass;
bool has_zs_ext = subpass->zs_attachment.idx != VK_ATTACHMENT_UNUSED;
unsigned tags = MALI_FBD_TAG_IS_MFBD;
batch->fb.info = cmdbuf->state.framebuffer;
batch->fb.desc =
pan_pool_alloc_desc_aggregate(&cmdbuf->desc_pool.base,
PAN_DESC(MULTI_TARGET_FRAMEBUFFER),
PAN_DESC_ARRAY(has_zs_ext ? 1 : 0, ZS_CRC_EXTENSION),
PAN_DESC_ARRAY(MAX2(subpass->color_count, 1), RENDER_TARGET));
/* Tag the pointer */
batch->fb.desc.gpu |= tags;
}
void
panvk_cmd_alloc_tls_desc(struct panvk_cmd_buffer *cmdbuf)
{
const struct panfrost_device *pdev =
&cmdbuf->device->physical_device->pdev;
struct panvk_batch *batch = cmdbuf->state.batch;
assert(batch);
if (batch->tls.gpu)
return;
if (!pan_is_bifrost(pdev) &&
cmdbuf->state.bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) {
panvk_cmd_alloc_fb_desc(cmdbuf);
batch->tls = batch->fb.desc;
batch->tls.gpu &= ~63ULL;
} else {
batch->tls =
pan_pool_alloc_desc(&cmdbuf->desc_pool.base, LOCAL_STORAGE);
}
}
static void
panvk_cmd_upload_sysval(struct panvk_cmd_buffer *cmdbuf,
unsigned id, union panvk_sysval_data *data)
{
switch (PAN_SYSVAL_TYPE(id)) {
case PAN_SYSVAL_VIEWPORT_SCALE:
panvk_sysval_upload_viewport_scale(&cmdbuf->state.viewport, data);
break;
case PAN_SYSVAL_VIEWPORT_OFFSET:
panvk_sysval_upload_viewport_offset(&cmdbuf->state.viewport, data);
break;
case PAN_SYSVAL_VERTEX_INSTANCE_OFFSETS:
/* TODO: support base_{vertex,instance} */
data->u32[0] = data->u32[1] = data->u32[2] = 0;
break;
default:
unreachable("Invalid static sysval");
}
}
static void
panvk_cmd_prepare_sysvals(struct panvk_cmd_buffer *cmdbuf)
{
struct panvk_descriptor_state *desc_state =
&cmdbuf->descriptors[cmdbuf->state.bind_point];
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
if (!pipeline->num_sysvals)
return;
for (unsigned i = 0; i < ARRAY_SIZE(desc_state->sysvals); i++) {
unsigned sysval_count = pipeline->sysvals[i].ids.sysval_count;
if (!sysval_count ||
(desc_state->sysvals[i] &&
!(cmdbuf->state.dirty & pipeline->sysvals[i].dirty_mask)))
continue;
struct panfrost_ptr sysvals =
pan_pool_alloc_aligned(&cmdbuf->desc_pool.base, sysval_count * 16, 16);
union panvk_sysval_data *data = sysvals.cpu;
for (unsigned s = 0; s < pipeline->sysvals[i].ids.sysval_count; s++) {
panvk_cmd_upload_sysval(cmdbuf, pipeline->sysvals[i].ids.sysvals[s],
&data[s]);
}
desc_state->sysvals[i] = sysvals.gpu;
}
}
static void
panvk_cmd_prepare_ubos(struct panvk_cmd_buffer *cmdbuf)
{
struct panvk_descriptor_state *desc_state =
&cmdbuf->descriptors[cmdbuf->state.bind_point];
const struct panvk_pipeline *pipeline =
cmdbuf->state.pipeline;
if (!pipeline->num_ubos || desc_state->ubos)
return;
panvk_cmd_prepare_sysvals(cmdbuf);
struct panfrost_ptr ubos =
pan_pool_alloc_desc_array(&cmdbuf->desc_pool.base,
pipeline->num_ubos,
UNIFORM_BUFFER);
panvk_emit_ubos(pipeline, desc_state, ubos.cpu);
desc_state->ubos = ubos.gpu;
}
static void
panvk_cmd_prepare_textures(struct panvk_cmd_buffer *cmdbuf)
{
struct panvk_descriptor_state *desc_state =
&cmdbuf->descriptors[cmdbuf->state.bind_point];
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
unsigned num_textures = pipeline->layout->num_textures;
if (!num_textures || desc_state->textures)
return;
const struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
unsigned tex_entry_size = pan_is_bifrost(pdev) ?
sizeof(struct mali_bifrost_texture_packed) :
sizeof(mali_ptr);
struct panfrost_ptr textures =
pan_pool_alloc_aligned(&cmdbuf->desc_pool.base,
num_textures * tex_entry_size,
tex_entry_size);
void *texture = textures.cpu;
for (unsigned i = 0; i < ARRAY_SIZE(desc_state->sets); i++) {
if (!desc_state->sets[i].set) continue;
memcpy(texture,
desc_state->sets[i].set->textures.midgard,
desc_state->sets[i].set->layout->num_textures *
tex_entry_size);
texture += desc_state->sets[i].set->layout->num_textures *
tex_entry_size;
}
desc_state->textures = textures.gpu;
}
static void
panvk_cmd_prepare_samplers(struct panvk_cmd_buffer *cmdbuf)
{
struct panvk_descriptor_state *desc_state =
&cmdbuf->descriptors[cmdbuf->state.bind_point];
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
unsigned num_samplers = pipeline->layout->num_samplers;
if (!num_samplers || desc_state->samplers)
return;
struct panfrost_ptr samplers =
pan_pool_alloc_desc_array(&cmdbuf->desc_pool.base,
num_samplers,
MIDGARD_SAMPLER);
struct mali_midgard_sampler_packed *sampler = samplers.cpu;
for (unsigned i = 0; i < ARRAY_SIZE(desc_state->sets); i++) {
if (!desc_state->sets[i].set) continue;
memcpy(sampler,
desc_state->sets[i].set->samplers,
desc_state->sets[i].set->layout->num_samplers *
sizeof(*sampler));
sampler += desc_state->sets[i].set->layout->num_samplers;
}
desc_state->samplers = samplers.gpu;
}
static void
panvk_draw_prepare_fs_rsd(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
if (!pipeline->fs.dynamic_rsd) {
draw->fs_rsd = pipeline->rsds[MESA_SHADER_FRAGMENT];
return;
}
if (!cmdbuf->state.fs_rsd) {
struct panfrost_ptr rsd =
pan_pool_alloc_desc_aggregate(&cmdbuf->desc_pool.base,
PAN_DESC(RENDERER_STATE),
PAN_DESC_ARRAY(pipeline->blend.state.rt_count,
BLEND));
struct mali_renderer_state_packed rsd_dyn;
panvk_emit_dyn_fs_rsd(cmdbuf->device, pipeline, &cmdbuf->state, &rsd_dyn);
pan_merge(rsd_dyn, pipeline->fs.rsd_template, RENDERER_STATE);
memcpy(rsd.cpu, &rsd_dyn, sizeof(rsd_dyn));
void *bd = rsd.cpu + MALI_RENDERER_STATE_LENGTH;
for (unsigned i = 0; i < pipeline->blend.state.rt_count; i++) {
if (pipeline->blend.constant[i].index != ~0) {
struct mali_blend_packed bd_dyn;
panvk_emit_blend_constant(cmdbuf->device, pipeline, i,
cmdbuf->state.blend.constants[i],
&bd_dyn);
pan_merge(bd_dyn, pipeline->blend.bd_template[i], BLEND);
memcpy(bd, &bd_dyn, sizeof(bd_dyn));
}
bd += MALI_BLEND_LENGTH;
}
cmdbuf->state.fs_rsd = rsd.gpu;
}
draw->fs_rsd = cmdbuf->state.fs_rsd;
}
void
panvk_cmd_get_bifrost_tiler_context(struct panvk_cmd_buffer *cmdbuf,
unsigned width, unsigned height)
{
struct panvk_batch *batch = cmdbuf->state.batch;
if (batch->tiler.bifrost_descs.cpu)
return;
batch->tiler.bifrost_descs =
pan_pool_alloc_desc_aggregate(&cmdbuf->desc_pool.base,
PAN_DESC(BIFROST_TILER),
PAN_DESC(BIFROST_TILER_HEAP));
panvk_emit_bifrost_tiler_context(cmdbuf->device, width, height,
&batch->tiler.bifrost_descs);
memcpy(&batch->tiler.templ.bifrost, batch->tiler.bifrost_descs.cpu,
sizeof(batch->tiler.templ.bifrost));
batch->tiler.ctx.bifrost = batch->tiler.bifrost_descs.gpu;
}
static void
panvk_draw_prepare_tiler_context(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
const struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
struct panvk_batch *batch = cmdbuf->state.batch;
if (pan_is_bifrost(pdev)) {
panvk_cmd_get_bifrost_tiler_context(cmdbuf,
batch->fb.info->width,
batch->fb.info->height);
} else {
panvk_cmd_get_midgard_polygon_list(cmdbuf,
batch->fb.info->width,
batch->fb.info->height,
true);
}
draw->tiler_ctx = &batch->tiler.ctx;
}
static void
panvk_draw_prepare_varyings(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
struct panvk_varyings_info *varyings = &cmdbuf->state.varyings;
panvk_varyings_alloc(varyings, &cmdbuf->varying_pool.base,
draw->vertex_count);
unsigned buf_count = panvk_varyings_buf_count(cmdbuf->device, varyings);
struct panfrost_ptr bufs =
pan_pool_alloc_desc_array(&cmdbuf->desc_pool.base,
buf_count, ATTRIBUTE_BUFFER);
panvk_emit_varying_bufs(cmdbuf->device, varyings, bufs.cpu);
if (BITSET_TEST(varyings->active, VARYING_SLOT_POS)) {
draw->position = varyings->buf[varyings->varying[VARYING_SLOT_POS].buf].address +
varyings->varying[VARYING_SLOT_POS].offset;
}
if (BITSET_TEST(varyings->active, VARYING_SLOT_PSIZ)) {
draw->psiz = varyings->buf[varyings->varying[VARYING_SLOT_PSIZ].buf].address +
varyings->varying[VARYING_SLOT_POS].offset;
} else if (pipeline->ia.topology == MALI_DRAW_MODE_LINES ||
pipeline->ia.topology == MALI_DRAW_MODE_LINE_STRIP ||
pipeline->ia.topology == MALI_DRAW_MODE_LINE_LOOP) {
draw->line_width = pipeline->dynamic_state_mask & PANVK_DYNAMIC_LINE_WIDTH ?
cmdbuf->state.rast.line_width : pipeline->rast.line_width;
} else {
draw->line_width = 1.0f;
}
draw->varying_bufs = bufs.gpu;
for (unsigned s = 0; s < MESA_SHADER_STAGES; s++) {
if (!varyings->stage[s].count) continue;
struct panfrost_ptr attribs =
pan_pool_alloc_desc_array(&cmdbuf->desc_pool.base,
varyings->stage[s].count,
ATTRIBUTE);
panvk_emit_varyings(cmdbuf->device, varyings, s, attribs.cpu);
draw->stages[s].varyings = attribs.gpu;
}
}
static void
panvk_draw_prepare_attributes(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
/* TODO: images */
const struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
if (!cmdbuf->state.pipeline->attribs.buf_count)
return;
if (cmdbuf->state.vb.attribs) {
draw->stages[MESA_SHADER_VERTEX].attributes = cmdbuf->state.vb.attribs;
draw->attribute_bufs = cmdbuf->state.vb.attrib_bufs;
return;
}
unsigned buf_count = cmdbuf->state.pipeline->attribs.buf_count +
(pan_is_bifrost(pdev) ? 1 : 0);
struct panfrost_ptr bufs =
pan_pool_alloc_desc_array(&cmdbuf->desc_pool.base,
buf_count * 2, ATTRIBUTE_BUFFER);
panvk_emit_attrib_bufs(cmdbuf->device,
&cmdbuf->state.pipeline->attribs,
cmdbuf->state.vb.bufs, cmdbuf->state.vb.count,
draw, bufs.cpu);
cmdbuf->state.vb.attrib_bufs = bufs.gpu;
struct panfrost_ptr attribs =
pan_pool_alloc_desc_array(&cmdbuf->desc_pool.base,
cmdbuf->state.pipeline->attribs.attrib_count,
ATTRIBUTE);
panvk_emit_attribs(cmdbuf->device, &cmdbuf->state.pipeline->attribs,
cmdbuf->state.vb.bufs, cmdbuf->state.vb.count,
attribs.cpu);
cmdbuf->state.vb.attribs = attribs.gpu;
draw->stages[MESA_SHADER_VERTEX].attributes = cmdbuf->state.vb.attribs;
draw->attribute_bufs = cmdbuf->state.vb.attrib_bufs;
}
static void
panvk_draw_prepare_viewport(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
if (pipeline->vpd) {
draw->viewport = pipeline->vpd;
} else if (cmdbuf->state.vpd) {
draw->viewport = cmdbuf->state.vpd;
} else {
struct panfrost_ptr vp =
pan_pool_alloc_desc(&cmdbuf->desc_pool.base, VIEWPORT);
const VkViewport *viewport =
pipeline->dynamic_state_mask & PANVK_DYNAMIC_VIEWPORT ?
&cmdbuf->state.viewport : &pipeline->viewport;
const VkRect2D *scissor =
pipeline->dynamic_state_mask & PANVK_DYNAMIC_SCISSOR ?
&cmdbuf->state.scissor : &pipeline->scissor;
panvk_emit_viewport(viewport, scissor, vp.cpu);
draw->viewport = cmdbuf->state.vpd = vp.gpu;
}
}
static void
panvk_draw_prepare_vertex_job(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
struct panvk_batch *batch = cmdbuf->state.batch;
struct panfrost_ptr ptr =
pan_pool_alloc_desc(&cmdbuf->desc_pool.base, COMPUTE_JOB);
util_dynarray_append(&batch->jobs, void *, ptr.cpu);
draw->jobs.vertex = ptr;
panvk_emit_vertex_job(cmdbuf->device,
cmdbuf->state.pipeline,
draw, ptr.cpu);
}
static void
panvk_draw_prepare_tiler_job(struct panvk_cmd_buffer *cmdbuf,
struct panvk_draw_info *draw)
{
const struct panfrost_device *pdev = &cmdbuf->device->physical_device->pdev;
struct panvk_batch *batch = cmdbuf->state.batch;
struct panfrost_ptr ptr =
pan_is_bifrost(pdev) ?
pan_pool_alloc_desc(&cmdbuf->desc_pool.base, BIFROST_TILER_JOB) :
pan_pool_alloc_desc(&cmdbuf->desc_pool.base, MIDGARD_TILER_JOB);
util_dynarray_append(&batch->jobs, void *, ptr.cpu);
draw->jobs.tiler = ptr;
panvk_emit_tiler_job(cmdbuf->device,
cmdbuf->state.pipeline,
draw, ptr.cpu);
}
void
panvk_CmdDraw(VkCommandBuffer commandBuffer,
uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
struct panvk_batch *batch = cmdbuf->state.batch;
/* There are only 16 bits in the descriptor for the job ID, make sure all
* the 3 (2 in Bifrost) jobs in this draw are in the same batch.
*/
if (batch->scoreboard.job_index >= (UINT16_MAX - 3)) {
panvk_cmd_close_batch(cmdbuf);
panvk_cmd_open_batch(cmdbuf);
batch = cmdbuf->state.batch;
}
if (cmdbuf->state.pipeline->fs.required)
panvk_cmd_alloc_fb_desc(cmdbuf);
panvk_cmd_alloc_tls_desc(cmdbuf);
panvk_cmd_prepare_ubos(cmdbuf);
panvk_cmd_prepare_textures(cmdbuf);
panvk_cmd_prepare_samplers(cmdbuf);
/* TODO: indexed draws */
struct panvk_draw_info draw = {
.first_vertex = firstVertex,
.vertex_count = vertexCount,
.first_instance = firstInstance,
.instance_count = instanceCount,
.padded_vertex_count = panfrost_padded_vertex_count(vertexCount),
.offset_start = firstVertex,
.tls = batch->tls.gpu,
.fb = batch->fb.desc.gpu,
.ubos = cmdbuf->descriptors[VK_PIPELINE_BIND_POINT_GRAPHICS].ubos,
.textures = cmdbuf->descriptors[VK_PIPELINE_BIND_POINT_GRAPHICS].textures,
.samplers = cmdbuf->descriptors[VK_PIPELINE_BIND_POINT_GRAPHICS].samplers,
};
panfrost_pack_work_groups_compute(&draw.invocation, 1, vertexCount,
instanceCount, 1, 1, 1, true, false);
panvk_draw_prepare_fs_rsd(cmdbuf, &draw);
panvk_draw_prepare_varyings(cmdbuf, &draw);
panvk_draw_prepare_attributes(cmdbuf, &draw);
panvk_draw_prepare_viewport(cmdbuf, &draw);
panvk_draw_prepare_tiler_context(cmdbuf, &draw);
panvk_draw_prepare_vertex_job(cmdbuf, &draw);
panvk_draw_prepare_tiler_job(cmdbuf, &draw);
const struct panvk_pipeline *pipeline = cmdbuf->state.pipeline;
unsigned vjob_id =
panfrost_add_job(&cmdbuf->desc_pool.base, &batch->scoreboard,
MALI_JOB_TYPE_VERTEX, false, false, 0, 0,
&draw.jobs.vertex, false);
if (pipeline->fs.required) {
panfrost_add_job(&cmdbuf->desc_pool.base, &batch->scoreboard,
MALI_JOB_TYPE_TILER, false, false, vjob_id, 0,
&draw.jobs.tiler, false);
}
/* Clear the dirty flags all at once */
cmdbuf->state.dirty = 0;
}
void
panvk_CmdDrawIndexed(VkCommandBuffer commandBuffer,
uint32_t indexCount,
uint32_t instanceCount,
uint32_t firstIndex,
int32_t vertexOffset,
uint32_t firstInstance)
{
panvk_stub();
}
void
panvk_CmdDrawIndirect(VkCommandBuffer commandBuffer,
VkBuffer _buffer,
VkDeviceSize offset,
uint32_t drawCount,
uint32_t stride)
{
panvk_stub();
}
void
panvk_CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer,
VkBuffer _buffer,
VkDeviceSize offset,
uint32_t drawCount,
uint32_t stride)
{
panvk_stub();
}
void
panvk_CmdDispatchBase(VkCommandBuffer commandBuffer,
uint32_t base_x,
uint32_t base_y,
uint32_t base_z,
uint32_t x,
uint32_t y,
uint32_t z)
{
panvk_stub();
}
void
panvk_CmdDispatch(VkCommandBuffer commandBuffer,
uint32_t x,
uint32_t y,
uint32_t z)
{
panvk_stub();
}
void
panvk_CmdDispatchIndirect(VkCommandBuffer commandBuffer,
VkBuffer _buffer,
VkDeviceSize offset)
{
panvk_stub();
}
void
panvk_CmdEndRenderPass2(VkCommandBuffer commandBuffer,
const VkSubpassEndInfoKHR *pSubpassEndInfo)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
panvk_cmd_close_batch(cmdbuf);
vk_free(&cmdbuf->pool->alloc, cmdbuf->state.clear);
cmdbuf->state.batch = NULL;
cmdbuf->state.pass = NULL;
cmdbuf->state.subpass = NULL;
cmdbuf->state.framebuffer = NULL;
cmdbuf->state.clear = NULL;
memset(&cmdbuf->state.compute, 0, sizeof(cmdbuf->state.compute));
}
void
panvk_CmdEndRenderPass(VkCommandBuffer cmd)
{
VkSubpassEndInfoKHR einfo = {
.sType = VK_STRUCTURE_TYPE_SUBPASS_END_INFO,
};
panvk_CmdEndRenderPass2(cmd, &einfo);
}
void
panvk_CmdPipelineBarrier(VkCommandBuffer commandBuffer,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags destStageMask,
VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount,
const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
/* Caches are flushed/invalidated at batch boundaries for now, nothing to do
* for memory barriers assuming we implement barriers with the creation of a
* new batch.
* FIXME: We can probably do better with a CacheFlush job that has the
* barrier flag set to true.
*/
if (cmdbuf->state.batch) {
panvk_cmd_close_batch(cmdbuf);
panvk_cmd_open_batch(cmdbuf);
}
}
static void
panvk_add_set_event_operation(struct panvk_cmd_buffer *cmdbuf,
struct panvk_event *event,
enum panvk_event_op_type type)
{
struct panvk_event_op op = {
.type = type,
.event = event,
};
if (cmdbuf->state.batch == NULL) {
/* No open batch, let's create a new one so this operation happens in
* the right order.
*/
panvk_cmd_open_batch(cmdbuf);
util_dynarray_append(&cmdbuf->state.batch->event_ops,
struct panvk_event_op,
op);
panvk_cmd_close_batch(cmdbuf);
} else {
/* Let's close the current batch so the operation executes before any
* future commands.
*/
util_dynarray_append(&cmdbuf->state.batch->event_ops,
struct panvk_event_op,
op);
panvk_cmd_close_batch(cmdbuf);
panvk_cmd_open_batch(cmdbuf);
}
}
static void
panvk_add_wait_event_operation(struct panvk_cmd_buffer *cmdbuf,
struct panvk_event *event)
{
struct panvk_event_op op = {
.type = PANVK_EVENT_OP_WAIT,
.event = event,
};
if (cmdbuf->state.batch == NULL) {
/* No open batch, let's create a new one and have it wait for this event. */
panvk_cmd_open_batch(cmdbuf);
util_dynarray_append(&cmdbuf->state.batch->event_ops,
struct panvk_event_op,
op);
} else {
/* Let's close the current batch so any future commands wait on the
* event signal operation.
*/
if (cmdbuf->state.batch->fragment_job ||
cmdbuf->state.batch->scoreboard.first_job) {
panvk_cmd_close_batch(cmdbuf);
panvk_cmd_open_batch(cmdbuf);
}
util_dynarray_append(&cmdbuf->state.batch->event_ops,
struct panvk_event_op,
op);
}
}
void
panvk_CmdSetEvent(VkCommandBuffer commandBuffer,
VkEvent _event,
VkPipelineStageFlags stageMask)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
VK_FROM_HANDLE(panvk_event, event, _event);
/* vkCmdSetEvent cannot be called inside a render pass */
assert(cmdbuf->state.pass == NULL);
panvk_add_set_event_operation(cmdbuf, event, PANVK_EVENT_OP_SET);
}
void
panvk_CmdResetEvent(VkCommandBuffer commandBuffer,
VkEvent _event,
VkPipelineStageFlags stageMask)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
VK_FROM_HANDLE(panvk_event, event, _event);
/* vkCmdResetEvent cannot be called inside a render pass */
assert(cmdbuf->state.pass == NULL);
panvk_add_set_event_operation(cmdbuf, event, PANVK_EVENT_OP_RESET);
}
void
panvk_CmdWaitEvents(VkCommandBuffer commandBuffer,
uint32_t eventCount,
const VkEvent *pEvents,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
uint32_t memoryBarrierCount,
const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount,
const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount,
const VkImageMemoryBarrier *pImageMemoryBarriers)
{
VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer);
assert(eventCount > 0);
for (uint32_t i = 0; i < eventCount; i++) {
VK_FROM_HANDLE(panvk_event, event, pEvents[i]);
panvk_add_wait_event_operation(cmdbuf, event);
}
}
void
panvk_CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask)
{
panvk_stub();
}