| /* |
| * Copyright 2018-2019 Alyssa Rosenzweig |
| * |
| * 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 "pan_bo.h" |
| #include "pan_context.h" |
| #include "pan_util.h" |
| |
| #include "util/format/u_format.h" |
| |
| static struct mali_sfbd_format |
| panfrost_sfbd_format(struct pipe_surface *surf) |
| { |
| /* Explode details on the format */ |
| |
| const struct util_format_description *desc = |
| util_format_description(surf->format); |
| |
| /* The swizzle for rendering is inverted from texturing */ |
| |
| unsigned char swizzle[4]; |
| panfrost_invert_swizzle(desc->swizzle, swizzle); |
| |
| struct mali_sfbd_format fmt = { |
| .unk1 = 0x1, |
| .swizzle = panfrost_translate_swizzle_4(swizzle), |
| .nr_channels = MALI_POSITIVE(desc->nr_channels), |
| .unk2 = 0x4, |
| .unk3 = 0xb, |
| }; |
| |
| if (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) |
| fmt.unk2 |= MALI_SFBD_FORMAT_SRGB; |
| |
| /* sRGB handled as a dedicated flag */ |
| enum pipe_format linearized = util_format_linear(surf->format); |
| |
| /* If RGB, we're good to go */ |
| if (util_format_is_unorm8(desc)) |
| return fmt; |
| |
| switch (linearized) { |
| case PIPE_FORMAT_B5G6R5_UNORM: |
| fmt.unk1 = 0x5; |
| fmt.nr_channels = MALI_POSITIVE(2); |
| fmt.unk2 = 0x5; |
| break; |
| |
| case PIPE_FORMAT_A4B4G4R4_UNORM: |
| case PIPE_FORMAT_B4G4R4A4_UNORM: |
| case PIPE_FORMAT_R4G4B4A4_UNORM: |
| fmt.unk1 = 0x4; |
| fmt.nr_channels = MALI_POSITIVE(1); |
| fmt.unk2 = 0x5; |
| break; |
| |
| default: |
| unreachable("Invalid format rendering"); |
| } |
| |
| return fmt; |
| } |
| |
| static void |
| panfrost_sfbd_clear( |
| struct panfrost_batch *batch, |
| struct mali_single_framebuffer *sfbd) |
| { |
| if (batch->clear & PIPE_CLEAR_COLOR) { |
| sfbd->clear_color_1 = batch->clear_color[0][0]; |
| sfbd->clear_color_2 = batch->clear_color[0][1]; |
| sfbd->clear_color_3 = batch->clear_color[0][2]; |
| sfbd->clear_color_4 = batch->clear_color[0][3]; |
| } |
| |
| if (batch->clear & PIPE_CLEAR_DEPTH) { |
| sfbd->clear_depth_1 = batch->clear_depth; |
| sfbd->clear_depth_2 = batch->clear_depth; |
| sfbd->clear_depth_3 = batch->clear_depth; |
| sfbd->clear_depth_4 = batch->clear_depth; |
| } |
| |
| if (batch->clear & PIPE_CLEAR_STENCIL) { |
| sfbd->clear_stencil = batch->clear_stencil; |
| } |
| |
| /* Set flags based on what has been cleared, for the SFBD case */ |
| /* XXX: What do these flags mean? */ |
| int clear_flags = 0x101100; |
| |
| if (!(batch->clear & ~(PIPE_CLEAR_COLOR | PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL))) { |
| /* On a tiler like this, it's fastest to clear all three buffers at once */ |
| |
| clear_flags |= MALI_CLEAR_FAST; |
| } else { |
| clear_flags |= MALI_CLEAR_SLOW; |
| |
| if (batch->clear & PIPE_CLEAR_STENCIL) |
| clear_flags |= MALI_CLEAR_SLOW_STENCIL; |
| } |
| |
| sfbd->clear_flags = clear_flags; |
| } |
| |
| static void |
| panfrost_sfbd_set_cbuf( |
| struct mali_single_framebuffer *fb, |
| struct pipe_surface *surf) |
| { |
| struct panfrost_resource *rsrc = pan_resource(surf->texture); |
| |
| unsigned level = surf->u.tex.level; |
| unsigned first_layer = surf->u.tex.first_layer; |
| assert(surf->u.tex.last_layer == first_layer); |
| signed stride = rsrc->slices[level].stride; |
| |
| mali_ptr base = panfrost_get_texture_address(rsrc, level, first_layer, 0); |
| |
| fb->format = panfrost_sfbd_format(surf); |
| |
| fb->framebuffer = base; |
| fb->stride = stride; |
| |
| if (rsrc->modifier == DRM_FORMAT_MOD_LINEAR) |
| fb->format.block = MALI_BLOCK_FORMAT_LINEAR; |
| else if (rsrc->modifier == DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) { |
| fb->format.block = MALI_BLOCK_FORMAT_TILED; |
| fb->stride *= 16; |
| } else { |
| fprintf(stderr, "Invalid render modifier\n"); |
| assert(0); |
| } |
| } |
| |
| static void |
| panfrost_sfbd_set_zsbuf( |
| struct mali_single_framebuffer *fb, |
| struct pipe_surface *surf) |
| { |
| struct panfrost_resource *rsrc = pan_resource(surf->texture); |
| struct panfrost_context *ctx = pan_context(surf->context); |
| |
| unsigned level = surf->u.tex.level; |
| assert(surf->u.tex.first_layer == 0); |
| |
| if (rsrc->modifier != DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED) |
| unreachable("Invalid render modifier."); |
| |
| fb->depth_buffer = rsrc->bo->gpu + rsrc->slices[level].offset; |
| fb->depth_stride = rsrc->slices[level].stride; |
| |
| /* No stencil? Job done. */ |
| if (!ctx->depth_stencil || !ctx->depth_stencil->base.stencil[0].enabled) |
| return; |
| |
| if (panfrost_is_z24s8_variant(surf->format)) { |
| /* Stencil data is interleaved with depth */ |
| fb->stencil_buffer = fb->depth_buffer; |
| fb->stencil_stride = fb->depth_stride; |
| } else if (surf->format == PIPE_FORMAT_Z32_FLOAT) { |
| /* No stencil, nothing to do */ |
| } else if (surf->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT) { |
| /* Stencil data in separate buffer */ |
| struct panfrost_resource *stencil = rsrc->separate_stencil; |
| struct panfrost_slice stencil_slice = stencil->slices[level]; |
| |
| fb->stencil_buffer = stencil->bo->gpu + stencil_slice.offset; |
| fb->stencil_stride = stencil_slice.stride; |
| } else |
| unreachable("Unsupported depth/stencil format."); |
| } |
| |
| |
| static struct mali_single_framebuffer |
| panfrost_emit_sfbd(struct panfrost_batch *batch, unsigned vertex_count) |
| { |
| struct panfrost_context *ctx = batch->ctx; |
| struct pipe_context *gallium = (struct pipe_context *) ctx; |
| struct panfrost_device *dev = pan_device(gallium->screen); |
| |
| unsigned width = batch->key.width; |
| unsigned height = batch->key.height; |
| |
| /* TODO: Why do we need to make the stack bigger than other platforms? */ |
| unsigned shift = panfrost_get_stack_shift(MAX2(batch->stack_size, 512)); |
| |
| struct mali_single_framebuffer framebuffer = { |
| .width = MALI_POSITIVE(width), |
| .height = MALI_POSITIVE(height), |
| .format = { |
| .unk3 = 0x3, |
| }, |
| .clear_flags = 0x1000, |
| .tiler = panfrost_emit_midg_tiler(batch, vertex_count), |
| }; |
| |
| struct mali_local_storage_packed lsp; |
| pan_pack(&lsp, LOCAL_STORAGE, ls) { |
| ls.tls_size = shift; |
| ls.wls_instances = MALI_LOCAL_STORAGE_NO_WORKGROUP_MEM; |
| ls.tls_base_pointer = |
| panfrost_batch_get_scratchpad(batch, |
| shift, |
| dev->thread_tls_alloc, |
| dev->core_count)->gpu; |
| } |
| framebuffer.shared_memory = lsp; |
| |
| return framebuffer; |
| } |
| |
| void |
| panfrost_attach_sfbd(struct panfrost_batch *batch, unsigned vertex_count) |
| { |
| struct mali_single_framebuffer sfbd = |
| panfrost_emit_sfbd(batch, vertex_count); |
| |
| memcpy(batch->framebuffer.cpu, &sfbd, sizeof(sfbd)); |
| } |
| |
| /* Creates an SFBD for the FRAGMENT section of the bound framebuffer */ |
| |
| mali_ptr |
| panfrost_sfbd_fragment(struct panfrost_batch *batch, bool has_draws) |
| { |
| struct mali_single_framebuffer fb = panfrost_emit_sfbd(batch, has_draws); |
| |
| panfrost_sfbd_clear(batch, &fb); |
| |
| /* SFBD does not support MRT natively; sanity check */ |
| assert(batch->key.nr_cbufs <= 1); |
| if (batch->key.nr_cbufs) { |
| struct pipe_surface *surf = batch->key.cbufs[0]; |
| struct panfrost_resource *rsrc = pan_resource(surf->texture); |
| struct panfrost_bo *bo = rsrc->bo; |
| |
| panfrost_sfbd_set_cbuf(&fb, surf); |
| |
| if (rsrc->checksummed) { |
| unsigned level = surf->u.tex.level; |
| struct panfrost_slice *slice = &rsrc->slices[level]; |
| |
| fb.checksum_stride = slice->checksum_stride; |
| fb.checksum = bo->gpu + slice->checksum_offset; |
| } |
| } |
| |
| if (batch->key.zsbuf) |
| panfrost_sfbd_set_zsbuf(&fb, batch->key.zsbuf); |
| |
| if (batch->requirements & PAN_REQ_MSAA) { |
| fb.format.unk1 |= MALI_SFBD_FORMAT_MSAA_A; |
| fb.format.unk2 |= MALI_SFBD_FORMAT_MSAA_B; |
| } |
| |
| return panfrost_pool_upload_aligned(&batch->pool, &fb, sizeof(fb), 64); |
| } |