feat(gpu): GMEM swap patch, unixnu --os, module pointers for the diagnostics batch
turnip-gmem-mutable-swap.patch moves GMEM loads and stores with the identity swap on the memory side and adds the render pass and submission rings; unixnu --os 17|26|27 selects the OS profile and its output directory.
This commit is contained in:
1 parent
a7c92c8cc6
commit
4b57b67251
9 files changed
+557
-5
No files matched your search
+1
-1
Submodule drivers/Adreno740 updated: 7cbe6e04bf...68fcf463a3.
+1
-1
Submodule drivers/AdrenoMetal updated: 8c7297ed5d...de38c6f507.
+1
-1
Submodule drivers/GopFramebuffer updated: ff091adfbd...b6e53e7b09.
@@ -0,0 +1,488 @@
|
||||
diff --git a/src/freedreno/fdl/fd6_view.cc b/src/freedreno/fdl/fd6_view.cc
|
||||
index 67f2095..c56a830 100644
|
||||
--- a/src/freedreno/fdl/fd6_view.cc
|
||||
+++ b/src/freedreno/fdl/fd6_view.cc
|
||||
@@ -537,7 +537,12 @@ fdl6_view_init(struct fdl6_view *view, const struct fdl_layout **layouts,
|
||||
tile_mode == TILE6_LINEAR && args->base_miplevel != layout->mip_levels - 1;
|
||||
|
||||
view->ubwc_enabled = ubwc_enabled;
|
||||
- view->is_mutable = layout->is_mutable;
|
||||
+ /* Mutability changes how a tiled image's swap is stored (MUTABLEEN above, only set for TILE6_3). A linear image keeps its
|
||||
+ * swap in memory whatever its formats, so it is not mutable for the code that loads and stores it through tile memory
|
||||
+ * (tu_clear_blit.cc and the GMEM input attachments of tu_cmd_buffer.cc take the swap and MUTABLEEN for a mutable view): it
|
||||
+ * swapped the tile's red and blue once more on the way to memory.
|
||||
+ */
|
||||
+ view->is_mutable = is_mutable;
|
||||
view->color_swap = color_swap;
|
||||
|
||||
view->RB_MRT_BUF_INFO =
|
||||
diff --git a/src/freedreno/vulkan/tu_clear_blit.cc b/src/freedreno/vulkan/tu_clear_blit.cc
|
||||
index ce3eca9..ad7a856 100644
|
||||
--- a/src/freedreno/vulkan/tu_clear_blit.cc
|
||||
+++ b/src/freedreno/vulkan/tu_clear_blit.cc
|
||||
@@ -206,6 +206,32 @@ format_to_ifmt(enum pipe_format format)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Tile memory holds a colour as the shader wrote it, whatever swap its image has in memory, so a load or store reads and writes
|
||||
+ * it with the identity swap. Per site (tu_gmem_side_stats): [0] transfers, [1] of them with a swap other than WZYX on the tile
|
||||
+ * side, [2] with MUTABLEEN, [3] of a linear image in memory. [1] is the counter that must stay zero for linear images.
|
||||
+ */
|
||||
+static uint64_t tu_gmem_side_counts[4][4];
|
||||
+
|
||||
+void
|
||||
+tu_note_gmem_side(unsigned site, bool linear, bool mutableen, unsigned swap)
|
||||
+{
|
||||
+ p_atomic_inc(&tu_gmem_side_counts[site][0]);
|
||||
+ if (swap != WZYX)
|
||||
+ p_atomic_inc(&tu_gmem_side_counts[site][1]);
|
||||
+ if (mutableen)
|
||||
+ p_atomic_inc(&tu_gmem_side_counts[site][2]);
|
||||
+ if (linear)
|
||||
+ p_atomic_inc(&tu_gmem_side_counts[site][3]);
|
||||
+}
|
||||
+
|
||||
+extern "C" __attribute__((visibility("default"))) void tu_gmem_side_stats(uint64_t out[16]);
|
||||
+extern "C" __attribute__((visibility("default"))) void
|
||||
+tu_gmem_side_stats(uint64_t out[16])
|
||||
+{
|
||||
+ for (unsigned i = 0; i < 16; i++)
|
||||
+ out[i] = p_atomic_read(&tu_gmem_side_counts[i / 4][i % 4]);
|
||||
+}
|
||||
+
|
||||
template <chip CHIP>
|
||||
static struct tu_native_format
|
||||
blit_format_texture(enum pipe_format format, enum a6xx_tile_mode tile_mode, bool is_mutable, bool gmem)
|
||||
@@ -543,12 +569,27 @@ r2d_src_buffer_unaligned(struct tu_cmd_buffer *cmd,
|
||||
TPL1_A2D_SRC_TEXTURE_PITCH(CHIP, .pitch = pitch));
|
||||
}
|
||||
|
||||
+/* GMEM holds a colour in the byte order its render target writes it in: a linear image's MRT swap (WXYZ for BGRA) is already applied
|
||||
+ * there, as system memory rendering writes it. A load or a store therefore moves the bytes as they are, with the identity swap on the
|
||||
+ * memory side too; the image's own swap on top exchanged red and blue against what system memory rendering writes (a frame mixing
|
||||
+ * both modes: the wallpaper of the lock screen 51 % exchanged in i-batch, centre redrawn in system memory correct). A mutable tiled
|
||||
+ * image (MUTABLEEN) keeps its swap.
|
||||
+ */
|
||||
+static inline uint32_t
|
||||
+tu_gmem_transfer_swap_fixup(uint32_t info, uint32_t swap_mask, uint32_t identity, bool mutable_view)
|
||||
+{
|
||||
+ return mutable_view ? info : ((info & ~swap_mask) | identity);
|
||||
+}
|
||||
+
|
||||
template <chip CHIP>
|
||||
static void
|
||||
-r2d_dst(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
- enum pipe_format src_format)
|
||||
+r2d_dst_impl(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
+ enum pipe_format src_format, bool gmem_transfer)
|
||||
{
|
||||
uint32_t dst_info = iview->RB_A2D_DEST_BUFFER_INFO;
|
||||
+ if (gmem_transfer)
|
||||
+ dst_info = tu_gmem_transfer_swap_fixup(dst_info, A6XX_RB_A2D_DEST_BUFFER_INFO_COLOR_SWAP__MASK,
|
||||
+ A6XX_RB_A2D_DEST_BUFFER_INFO_COLOR_SWAP(WZYX), iview->is_mutable);
|
||||
enum a6xx_format fmt = (enum a6xx_format) pkt_field_get(
|
||||
A6XX_RB_A2D_DEST_BUFFER_INFO_COLOR_FORMAT, dst_info);
|
||||
enum pipe_format dst_format = iview->format;
|
||||
@@ -563,6 +604,14 @@ r2d_dst(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
tu_cs_image_flag_ref(cs, iview, layer);
|
||||
}
|
||||
|
||||
+template <chip CHIP>
|
||||
+static void
|
||||
+r2d_dst(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
+ enum pipe_format src_format)
|
||||
+{
|
||||
+ r2d_dst_impl<CHIP>(cs, iview, layer, src_format, false);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
r2d_dst_depth(struct tu_cs *cs, const struct tu_image_view *iview, uint32_t layer)
|
||||
{
|
||||
@@ -1594,6 +1643,7 @@ r3d_src_gmem_load(struct tu_cmd_buffer *cmd,
|
||||
const struct tu_image_view *iview,
|
||||
uint32_t layer)
|
||||
{
|
||||
+ tu_note_gmem_side(1, iview->image->layout[0].tile_mode == TILE6_LINEAR, false, WZYX);
|
||||
r3d_src_load<CHIP>(cmd, cs, iview, layer, VK_FILTER_NEAREST, true);
|
||||
}
|
||||
|
||||
@@ -1631,6 +1681,8 @@ r3d_src_gmem(struct tu_cmd_buffer *cmd,
|
||||
|
||||
if (!iview->view.is_mutable)
|
||||
tu_desc_set_swap<CHIP>(desc, WZYX);
|
||||
+ tu_note_gmem_side(0, iview->image->layout[0].tile_mode == TILE6_LINEAR, false,
|
||||
+ iview->view.is_mutable ? iview->view.color_swap : WZYX);
|
||||
|
||||
/* If FDM offset is used, the last row and column extend beyond the
|
||||
* framebuffer but are shifted over when storing. Expand the width and
|
||||
@@ -1672,10 +1724,13 @@ r3d_src_gmem(struct tu_cmd_buffer *cmd,
|
||||
|
||||
template <chip CHIP>
|
||||
static void
|
||||
-r3d_dst(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
- enum pipe_format src_format)
|
||||
+r3d_dst_impl(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
+ enum pipe_format src_format, bool gmem_transfer)
|
||||
{
|
||||
uint32_t mrt_buf_info = iview->RB_MRT_BUF_INFO;
|
||||
+ if (gmem_transfer)
|
||||
+ mrt_buf_info = tu_gmem_transfer_swap_fixup(mrt_buf_info, A6XX_RB_MRT_BUF_INFO_COLOR_SWAP__MASK,
|
||||
+ A6XX_RB_MRT_BUF_INFO_COLOR_SWAP(WZYX), iview->is_mutable);
|
||||
|
||||
enum a6xx_format fmt = (enum a6xx_format) pkt_field_get(
|
||||
A6XX_RB_MRT_BUF_INFO_COLOR_FORMAT, mrt_buf_info);
|
||||
@@ -1705,6 +1760,14 @@ r3d_dst(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
tu_cs_emit_regs(cs, GRAS_SU_RENDER_CNTL(CHIP));
|
||||
}
|
||||
|
||||
+template <chip CHIP>
|
||||
+static void
|
||||
+r3d_dst(struct tu_cs *cs, const struct fdl6_view *iview, uint32_t layer,
|
||||
+ enum pipe_format src_format)
|
||||
+{
|
||||
+ r3d_dst_impl<CHIP>(cs, iview, layer, src_format, false);
|
||||
+}
|
||||
+
|
||||
template <chip CHIP>
|
||||
static void
|
||||
r3d_dst_depth(struct tu_cs *cs, const struct tu_image_view *iview, uint32_t layer)
|
||||
@@ -1792,6 +1855,8 @@ r3d_dst_gmem(struct tu_cmd_buffer *cmd, struct tu_cs *cs,
|
||||
} else {
|
||||
RB_MRT_BUF_INFO = iview->view.RB_MRT_BUF_INFO;
|
||||
gmem_offset = tu_attachment_gmem_offset(cmd, att, layer);
|
||||
+ RB_MRT_BUF_INFO = tu_gmem_transfer_swap_fixup(RB_MRT_BUF_INFO, A6XX_RB_MRT_BUF_INFO_COLOR_SWAP__MASK,
|
||||
+ A6XX_RB_MRT_BUF_INFO_COLOR_SWAP(WZYX), iview->view.is_mutable);
|
||||
}
|
||||
|
||||
/* On a7xx we must always use FMT6_Z24_UNORM_S8_UINT_AS_R8G8B8A8. See
|
||||
@@ -2339,7 +2404,8 @@ event_blit_run(struct tu_cmd_buffer *cmd,
|
||||
struct tu_cs *cs,
|
||||
const struct tu_render_pass_attachment *att,
|
||||
const event_blit_dst_view *blt_view,
|
||||
- bool separate_stencil)
|
||||
+ bool separate_stencil,
|
||||
+ bool gmem_transfer = false)
|
||||
{
|
||||
tu_cs_emit_pkt4(cs, REG_A6XX_RB_RESOLVE_SYSTEM_BUFFER_INFO, 4);
|
||||
if (blt_view->image->vk.format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
|
||||
@@ -2357,7 +2423,12 @@ event_blit_run(struct tu_cmd_buffer *cmd,
|
||||
tu_cs_emit(cs, A6XX_RB_RESOLVE_SYSTEM_BUFFER_PITCH(blt_view->stencil_pitch).value);
|
||||
}
|
||||
} else {
|
||||
- tu_cs_emit(cs, blt_view->view->RB_RESOLVE_SYSTEM_BUFFER_INFO);
|
||||
+ tu_cs_emit(cs, gmem_transfer ?
|
||||
+ tu_gmem_transfer_swap_fixup(blt_view->view->RB_RESOLVE_SYSTEM_BUFFER_INFO,
|
||||
+ A6XX_RB_RESOLVE_SYSTEM_BUFFER_INFO_COLOR_SWAP__MASK,
|
||||
+ A6XX_RB_RESOLVE_SYSTEM_BUFFER_INFO_COLOR_SWAP(WZYX),
|
||||
+ blt_view->view->is_mutable) :
|
||||
+ blt_view->view->RB_RESOLVE_SYSTEM_BUFFER_INFO);
|
||||
tu_cs_image_ref_2d<CHIP>(cs, blt_view->view, blt_view->layer, false);
|
||||
|
||||
tu_cs_emit_pkt4(cs, REG_A6XX_RB_RESOLVE_SYSTEM_FLAG_BUFFER_BASE, 3);
|
||||
@@ -5523,7 +5594,7 @@ tu_emit_blit(struct tu_cmd_buffer *cmd,
|
||||
tu6_emit_blit_scissor(cmd, cs, i, align_scissor);
|
||||
}
|
||||
event_blit_dst_view blt_view = blt_view_from_tu_view(iview, i);
|
||||
- event_blit_run<CHIP>(cmd, cs, attachment, &blt_view, separate_stencil);
|
||||
+ event_blit_run<CHIP>(cmd, cs, attachment, &blt_view, separate_stencil, true);
|
||||
}
|
||||
|
||||
tu_flush_for_access(&cmd->state.cache, TU_ACCESS_BLIT_WRITE_GMEM,
|
||||
@@ -5839,7 +5910,7 @@ store_cp_blit(struct tu_cmd_buffer *cmd,
|
||||
r2d_dst_stencil(cs, dst_iview, layer);
|
||||
}
|
||||
} else {
|
||||
- r2d_dst<CHIP>(cs, &dst_iview->view, layer, src_format);
|
||||
+ r2d_dst_impl<CHIP>(cs, &dst_iview->view, layer, src_format, true);
|
||||
}
|
||||
|
||||
/* Note: we compute the swap here instead of using the color_swap as
|
||||
@@ -5867,6 +5938,8 @@ store_cp_blit(struct tu_cmd_buffer *cmd,
|
||||
src_height += tiling->tile0.height;
|
||||
}
|
||||
|
||||
+ tu_note_gmem_side(2, dst_iview->image->layout[0].tile_mode == TILE6_LINEAR, src_iview->view.is_mutable, fmt.swap);
|
||||
+
|
||||
uint64_t va = gmem_offset;
|
||||
if (CHIP < A8XX) {
|
||||
/* For gen8, address is simply gmem_offset if tile_mode is gmem
|
||||
@@ -5957,7 +6030,7 @@ store_3d_blit(struct tu_cmd_buffer *cmd,
|
||||
r3d_dst_stencil<CHIP>(cs, dst_iview, layer);
|
||||
}
|
||||
} else {
|
||||
- r3d_dst<CHIP>(cs, &dst_iview->view, layer, src_format);
|
||||
+ r3d_dst_impl<CHIP>(cs, &dst_iview->view, layer, src_format, true);
|
||||
}
|
||||
|
||||
r3d_src_gmem<CHIP>(cmd, cs, src_iview, src_format, dst_format, gmem_offset, cpp);
|
||||
diff --git a/src/freedreno/vulkan/tu_clear_blit.h b/src/freedreno/vulkan/tu_clear_blit.h
|
||||
index 62d1e43..7d59dc3 100644
|
||||
--- a/src/freedreno/vulkan/tu_clear_blit.h
|
||||
+++ b/src/freedreno/vulkan/tu_clear_blit.h
|
||||
@@ -103,6 +103,12 @@ tu_choose_gmem_layout(struct tu_cmd_buffer *cmd);
|
||||
bool
|
||||
tu_attachment_unpadded(struct tu_cmd_buffer *cmd, const struct tu_image_view *iview);
|
||||
|
||||
+/* Telemetry of the tile-memory side of GMEM loads and stores (tu_gmem_side_stats): site 0 a 3D store's source, 1 a 3D load's source,
|
||||
+ * 2 a 2D store's source, 3 an input attachment read from tile memory.
|
||||
+ */
|
||||
+void
|
||||
+tu_note_gmem_side(unsigned site, bool linear, bool mutableen, unsigned swap);
|
||||
+
|
||||
void
|
||||
tu_cmd_fill_buffer_addr(VkCommandBuffer commandBuffer,
|
||||
VkDeviceAddress dstAddr,
|
||||
diff --git a/src/freedreno/vulkan/tu_cmd_buffer.cc b/src/freedreno/vulkan/tu_cmd_buffer.cc
|
||||
index 33a22aa..1a0fdc8 100644
|
||||
--- a/src/freedreno/vulkan/tu_cmd_buffer.cc
|
||||
+++ b/src/freedreno/vulkan/tu_cmd_buffer.cc
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "tu_subsampled_image.h"
|
||||
#include "tu_tile_config.h"
|
||||
#include "tu_tracepoints.h"
|
||||
+#include "util/os_time.h"
|
||||
|
||||
enum tu_cmd_buffer_status {
|
||||
TU_CMD_BUFFER_STATUS_IDLE = 0,
|
||||
@@ -2899,6 +2900,8 @@ tu_emit_input_attachments(struct tu_cmd_buffer *cmd,
|
||||
|
||||
if (!iview->view.is_mutable)
|
||||
tu_desc_set_swap<CHIP>(dst, WZYX);
|
||||
+ tu_note_gmem_side(3, iview->image->layout[0].tile_mode == TILE6_LINEAR, false,
|
||||
+ iview->view.is_mutable ? iview->view.color_swap : WZYX);
|
||||
|
||||
/* If FDM offset is used, the last row and column extend beyond the
|
||||
* framebuffer but are shifted over when storing. Expand the width and
|
||||
@@ -4056,6 +4059,87 @@ tu_cmd_render_sysmem(struct tu_cmd_buffer *cmd,
|
||||
*/
|
||||
static uint64_t tu_render_mode_counts[8];
|
||||
|
||||
+#ifdef __APPLE__
|
||||
+/* The clock every trace of the stack uses (CLOCK_UPTIME_RAW: the kernel's mach_absolute_time and the frontend's nowUs()), so rows join by time. */
|
||||
+static inline uint64_t tu_trace_clock_ns(void) {return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);}
|
||||
+#else
|
||||
+static inline uint64_t tu_trace_clock_ns(void) {return os_time_get_nano();}
|
||||
+#endif
|
||||
+
|
||||
+/* The last 512 render passes (tu_pass_ring_read), for the process's telemetry: when, how it was rendered and what its first colour
|
||||
+ * attachment is, so the passes of one frame and the byte order they write can be read back.
|
||||
+ */
|
||||
+struct tu_pass_rec {
|
||||
+ uint64_t ns; /* os_time_get_nano() when the pass was recorded */
|
||||
+ uint32_t seq;
|
||||
+ uint16_t width, height;
|
||||
+ uint8_t mode; /* 0 GMEM, 1 SYSMEM chosen by the autotuner, 2 SYSMEM for another reason */
|
||||
+ uint8_t colors; /* colour attachments of the first subpass */
|
||||
+ uint8_t flags; /* 1 mutable view, 2 linear image, 4 unpadded (loaded and stored over its texels), 8 loaded, 16 stored, 32 FDM */
|
||||
+ uint8_t swap; /* the colour swap of the first colour attachment's view */
|
||||
+ uint16_t draws;
|
||||
+ uint16_t format; /* VkFormat of the first colour attachment */
|
||||
+ uint32_t kpx;
|
||||
+};
|
||||
+static_assert(sizeof(struct tu_pass_rec) == 32, "pass record");
|
||||
+static struct tu_pass_rec tu_pass_ring[512];
|
||||
+static uint32_t tu_pass_seq;
|
||||
+
|
||||
+static void
|
||||
+tu_note_pass(struct tu_cmd_buffer *cmd, bool sysmem, unsigned slot, uint64_t kpx)
|
||||
+{
|
||||
+ struct tu_pass_rec rec = {};
|
||||
+ const VkExtent2D extent = cmd->state.render_areas[0].extent;
|
||||
+ rec.ns = tu_trace_clock_ns();
|
||||
+ rec.width = extent.width;
|
||||
+ rec.height = extent.height;
|
||||
+ rec.mode = !sysmem ? 0 : slot == 2 ? 1 : 2;
|
||||
+ rec.draws = MIN2(cmd->state.rp.drawcall_count, 0xffff);
|
||||
+ rec.kpx = kpx;
|
||||
+ rec.flags = cmd->state.pass->has_fdm ? 32 : 0;
|
||||
+ const struct tu_subpass *subpass = &cmd->state.pass->subpasses[0];
|
||||
+ rec.colors = subpass->color_count;
|
||||
+ for (uint32_t i = 0; i < subpass->color_count; i++) {
|
||||
+ const uint32_t a = subpass->color_attachments[i].attachment;
|
||||
+ if (a == VK_ATTACHMENT_UNUSED || !cmd->state.attachments[a])
|
||||
+ continue;
|
||||
+ const struct tu_image_view *iview = cmd->state.attachments[a];
|
||||
+ const struct tu_render_pass_attachment *att = &cmd->state.pass->attachments[a];
|
||||
+ rec.format = (uint16_t) iview->vk.format;
|
||||
+ rec.swap = iview->view.color_swap;
|
||||
+ rec.flags |= (iview->view.is_mutable ? 1 : 0) | (iview->image->layout[0].tile_mode == TILE6_LINEAR ? 2 : 0) |
|
||||
+ (tu_attachment_unpadded(cmd, iview) ? 4 : 0) | (att->load ? 8 : 0) | (att->store ? 16 : 0);
|
||||
+ break;
|
||||
+ }
|
||||
+ const uint32_t seq = p_atomic_inc_return(&tu_pass_seq);
|
||||
+ rec.seq = seq;
|
||||
+ tu_pass_ring[seq % 512] = rec;
|
||||
+}
|
||||
+
|
||||
+/* Copies up to max records with a sequence number of from or more (oldest first) and returns how many; *newest is the newest
|
||||
+ * sequence number handed out.
|
||||
+ */
|
||||
+extern "C" __attribute__((visibility("default"))) uint32_t tu_pass_ring_read(uint32_t from, struct tu_pass_rec *out, uint32_t max,
|
||||
+ uint32_t *newest);
|
||||
+extern "C" __attribute__((visibility("default"))) uint32_t
|
||||
+tu_pass_ring_read(uint32_t from, struct tu_pass_rec *out, uint32_t max, uint32_t *newest)
|
||||
+{
|
||||
+ const uint32_t last = p_atomic_read(&tu_pass_seq);
|
||||
+ *newest = last;
|
||||
+ uint32_t first = from ? from : 1;
|
||||
+ if (first > last)
|
||||
+ return 0;
|
||||
+ if (last - first >= 512)
|
||||
+ first = last - 511;
|
||||
+ uint32_t n = 0;
|
||||
+ for (uint32_t s = first; s <= last && n < max; s++) {
|
||||
+ const struct tu_pass_rec rec = tu_pass_ring[s % 512];
|
||||
+ if (rec.seq == s)
|
||||
+ out[n++] = rec;
|
||||
+ }
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
static void
|
||||
tu_note_render_mode(struct tu_cmd_buffer *cmd, bool sysmem)
|
||||
{
|
||||
@@ -4066,6 +4150,7 @@ tu_note_render_mode(struct tu_cmd_buffer *cmd, bool sysmem)
|
||||
!sysmem ? 0 : reason && !strcmp(reason, "Autotune selected sysmem") ? 2 : 4;
|
||||
p_atomic_inc(&tu_render_mode_counts[slot]);
|
||||
p_atomic_add(&tu_render_mode_counts[slot + 1], kpx);
|
||||
+ tu_note_pass(cmd, sysmem, slot, kpx);
|
||||
if (sysmem)
|
||||
return;
|
||||
for (unsigned i = 0; i < cmd->state.pass->attachment_count; i++) {
|
||||
diff --git a/src/freedreno/vulkan/tu_knl_darwin.cc b/src/freedreno/vulkan/tu_knl_darwin.cc
|
||||
index 0209408..67b7e2c 100644
|
||||
--- a/src/freedreno/vulkan/tu_knl_darwin.cc
|
||||
+++ b/src/freedreno/vulkan/tu_knl_darwin.cc
|
||||
@@ -1283,11 +1283,57 @@ darwin_note_fence(uint32_t fence)
|
||||
dw.last_fence = fence;
|
||||
}
|
||||
|
||||
+#ifdef __APPLE__
|
||||
+/* The clock every trace of the stack uses (CLOCK_UPTIME_RAW: the kernel's mach_absolute_time and the frontend's nowUs()), so rows join by time. */
|
||||
+static inline uint64_t tu_trace_clock_ns(void) {return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);}
|
||||
+#else
|
||||
+static inline uint64_t tu_trace_clock_ns(void) {return os_time_get_nano();}
|
||||
+#endif
|
||||
+
|
||||
+/* The last 512 kernel submissions of this process (tu_darwin_submit_ring_read): when each was handed to the kernel, which fence it got,
|
||||
+ * and where its time went before and inside the call (waiting for an unsubmitted sync, the kernel's ring having no room).
|
||||
+ */
|
||||
+struct tu_darwin_submit_rec {
|
||||
+ uint64_t ns; /* os_time_get_nano() when darwin_queue_submit began */
|
||||
+ uint32_t seq;
|
||||
+ uint32_t fence; /* the kernel fence of the last chunk, 0 when nothing was run */
|
||||
+ uint32_t sync_wait_us; /* waiting for a sync that was not yet submitted */
|
||||
+ uint32_t call_us; /* the kernel calls, ring-space waits included */
|
||||
+ uint32_t space_wait_us; /* of call_us, waiting for ring space */
|
||||
+ uint16_t busy; /* kIOReturnBusy answers */
|
||||
+ uint8_t chunks;
|
||||
+ uint8_t buffers; /* indirect buffers in all chunks, 255 at most */
|
||||
+};
|
||||
+static_assert(sizeof(struct tu_darwin_submit_rec) == 32, "submit record");
|
||||
+static struct tu_darwin_submit_rec darwin_submit_ring[512];
|
||||
+static uint32_t darwin_submit_seq;
|
||||
+
|
||||
+extern "C" __attribute__((visibility("default"))) uint32_t tu_darwin_submit_ring_read(uint32_t from, struct tu_darwin_submit_rec *out,
|
||||
+ uint32_t max, uint32_t *newest);
|
||||
+extern "C" __attribute__((visibility("default"))) uint32_t
|
||||
+tu_darwin_submit_ring_read(uint32_t from, struct tu_darwin_submit_rec *out, uint32_t max, uint32_t *newest)
|
||||
+{
|
||||
+ const uint32_t last = p_atomic_read(&darwin_submit_seq);
|
||||
+ *newest = last;
|
||||
+ uint32_t first = from ? from : 1;
|
||||
+ if (first > last)
|
||||
+ return 0;
|
||||
+ if (last - first >= 512)
|
||||
+ first = last - 511;
|
||||
+ uint32_t n = 0;
|
||||
+ for (uint32_t s = first; s <= last && n < max; s++) {
|
||||
+ const struct tu_darwin_submit_rec rec = darwin_submit_ring[s % 512];
|
||||
+ if (rec.seq == s)
|
||||
+ out[n++] = rec;
|
||||
+ }
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
/* One kernel submission of up to NativeAdrenoAbiMaxBuffers indirect buffers; while the kernel's ring has no room (kIOReturnBusy, as msm
|
||||
* waits for ring space) it waits for this client's oldest job, or yields when none is in flight. */
|
||||
static VkResult
|
||||
darwin_submit_chunk(struct tu_queue *queue, const NativeAdrenoAbiSubmitBuffer *entries,
|
||||
- unsigned count, uint32_t *fence)
|
||||
+ unsigned count, uint32_t *fence, struct tu_darwin_submit_rec *rec)
|
||||
{
|
||||
struct tu_device *dev = queue->device;
|
||||
NativeAdrenoAbiSubmit req = {};
|
||||
@@ -1298,21 +1344,26 @@ darwin_submit_chunk(struct tu_queue *queue, const NativeAdrenoAbiSubmitBuffer *e
|
||||
|
||||
for (;;) {
|
||||
NativeAdrenoAbiSubmitReply reply = {};
|
||||
+ const uint64_t began = tu_trace_clock_ns();
|
||||
kern_return_t kr = dw_call(NativeAdrenoMethodSubmit, &req, sizeof(req), &reply, sizeof(reply));
|
||||
+ rec->call_us += (uint32_t)MIN2((tu_trace_clock_ns() - began) / 1000, 0xffffffffull - rec->call_us);
|
||||
if (kr == KERN_SUCCESS) {
|
||||
*fence = (uint32_t)reply.fence;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
if (kr != kIOReturnBusy)
|
||||
return vk_device_set_lost(&dev->vk, "submit failed: 0x%x", kr);
|
||||
+ rec->busy++;
|
||||
|
||||
pthread_mutex_lock(&dw.lock);
|
||||
uint32_t oldest = dw.inflight_count ? dw.inflight[0] : 0;
|
||||
pthread_mutex_unlock(&dw.lock);
|
||||
+ const uint64_t waited = tu_trace_clock_ns();
|
||||
if (!oldest)
|
||||
sched_yield();
|
||||
else if (dw_wait_fence_locked_free(dev, oldest, UINT64_MAX) != VK_SUCCESS)
|
||||
return vk_device_set_lost(&dev->vk, "submit: waiting for ring space failed");
|
||||
+ rec->space_wait_us += (uint32_t)((tu_trace_clock_ns() - waited) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1330,12 +1381,15 @@ darwin_queue_submit(struct tu_queue *queue, void *_submit,
|
||||
* fence is therefore met by the ring itself, as msm_gem_submit() skips fences of the submitting ring's own context: the CPU does
|
||||
* not wait for that work to complete. A sync not yet resolved (wait-before-signal) is waited for until it is submitted, never for
|
||||
* its completion. */
|
||||
+ struct tu_darwin_submit_rec rec = {};
|
||||
+ rec.ns = tu_trace_clock_ns();
|
||||
for (uint32_t i = 0; i < wait_count; i++) {
|
||||
struct vk_darwin_sync *s = container_of(waits[i].sync, struct vk_darwin_sync, vk);
|
||||
/* tu_queue_submit() holds submit_mutex here (tu_queue.cc), which the wait releases while it sleeps. */
|
||||
while (s->state == DARWIN_SYNC_UNSIGNALED)
|
||||
pthread_cond_wait(&dev->timeline_cond, &dev->submit_mutex);
|
||||
}
|
||||
+ rec.sync_wait_us = (uint32_t)((tu_trace_clock_ns() - rec.ns) / 1000);
|
||||
|
||||
uint32_t fence = 0;
|
||||
const unsigned total = util_dynarray_num_elements(&submit->commands, NativeAdrenoAbiSubmitBuffer);
|
||||
@@ -1345,20 +1399,29 @@ darwin_queue_submit(struct tu_queue *queue, void *_submit,
|
||||
for (unsigned at = 0; at < total; at += NativeAdrenoAbiMaxBuffers) {
|
||||
unsigned count = MIN2(total - at, (unsigned)NativeAdrenoAbiMaxBuffers);
|
||||
uint32_t chunk_fence = 0;
|
||||
- VkResult result = darwin_submit_chunk(queue, all + at, count, &chunk_fence);
|
||||
+ VkResult result = darwin_submit_chunk(queue, all + at, count, &chunk_fence, &rec);
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
+ rec.chunks++;
|
||||
pthread_mutex_lock(&dw.lock);
|
||||
darwin_note_fence(chunk_fence);
|
||||
pthread_mutex_unlock(&dw.lock);
|
||||
fence = chunk_fence;
|
||||
}
|
||||
p_atomic_set(&queue->fence, fence);
|
||||
+ rec.buffers = MIN2(total, 255u);
|
||||
} else {
|
||||
/* Nothing to run: the signals resolve with the last earlier submission. */
|
||||
fence = p_atomic_read(&queue->fence) > 0 ? (uint32_t)p_atomic_read(&queue->fence) : 0;
|
||||
}
|
||||
|
||||
+ rec.fence = fence;
|
||||
+ {
|
||||
+ const uint32_t seq = p_atomic_inc_return(&darwin_submit_seq);
|
||||
+ rec.seq = seq;
|
||||
+ darwin_submit_ring[seq % 512] = rec;
|
||||
+ }
|
||||
+
|
||||
for (uint32_t i = 0; i < signal_count; i++) {
|
||||
struct vk_darwin_sync *s = container_of(signals[i].sync, struct vk_darwin_sync, vk);
|
||||
s->fence = fence;
|
||||
@@ -54,7 +54,8 @@
|
||||
"patches/mesa/turnip-empty-scissor.patch",
|
||||
"patches/mesa/turnip-builtin-shader-pack.patch",
|
||||
"patches/mesa/turnip-host-import-images.patch",
|
||||
"patches/mesa/turnip-render-mode-for-attachment-memory.patch"
|
||||
"patches/mesa/turnip-render-mode-for-attachment-memory.patch",
|
||||
"patches/mesa/turnip-gmem-mutable-swap.patch"
|
||||
],
|
||||
"integer_fma_reference": {
|
||||
"repository": "https://github.com/ucb-bar/berkeley-softfloat-3",
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerate patches/mesa/turnip-gmem-mutable-swap.patch from the working Mesa tree.
|
||||
|
||||
The patch is the difference between "pinned Mesa + the earlier recorded patches" and the working tree for the files it
|
||||
touches (the swap of a linear mutable view in GMEM loads and stores, and the render pass telemetry), so it applies last. Edit the Mesa
|
||||
tree, run this, then run build-adreno-compiler-host.py to prove the tree equals the recorded patches."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MESA = ROOT / '.cache/mesa-work'
|
||||
MINE = ['src/freedreno/fdl/fd6_view.cc', 'src/freedreno/vulkan/tu_clear_blit.cc', 'src/freedreno/vulkan/tu_cmd_buffer.cc',
|
||||
'src/freedreno/vulkan/tu_clear_blit.h', 'src/freedreno/vulkan/tu_knl_darwin.cc']
|
||||
OUT = ROOT / 'patches/mesa/turnip-gmem-mutable-swap.patch'
|
||||
listed = json.loads((ROOT / 'scripts/data/adreno-compiler-inputs.json').read_text())['patches']
|
||||
others = listed[:listed.index(str(OUT.relative_to(ROOT)))]
|
||||
|
||||
|
||||
def git(tmp, *args, check=True):
|
||||
return subprocess.run(['git', '-c', 'user.email=a@b', '-c', 'user.name=n', *args], cwd=tmp, check=check,
|
||||
capture_output=True, text=True).stdout
|
||||
|
||||
|
||||
tmp = Path(tempfile.mkdtemp(dir=ROOT / '.cache'))
|
||||
try:
|
||||
git(tmp, 'init', '-q')
|
||||
for f in MINE:
|
||||
shown = subprocess.run(['git', 'show', 'HEAD:' + f], cwd=MESA, capture_output=True)
|
||||
if shown.returncode: # a file an earlier recorded patch creates
|
||||
continue
|
||||
(tmp / f).parent.mkdir(parents=True, exist_ok=True)
|
||||
(tmp / f).write_bytes(shown.stdout)
|
||||
git(tmp, 'add', '-A')
|
||||
git(tmp, 'commit', '-q', '-m', 'base')
|
||||
for patch in others:
|
||||
for f in MINE:
|
||||
git(tmp, 'apply', '--include=' + f, str(ROOT / patch), check=False)
|
||||
git(tmp, 'add', '-A')
|
||||
git(tmp, 'commit', '-q', '-m', 'others', '--allow-empty')
|
||||
for f in MINE:
|
||||
shutil.copy(MESA / f, tmp / f)
|
||||
OUT.write_text(git(tmp, 'diff', '--binary'))
|
||||
finally:
|
||||
shutil.rmtree(tmp)
|
||||
print('wrote', OUT.relative_to(ROOT), len(OUT.read_text().splitlines()), 'lines')
|
||||
@@ -17,6 +17,7 @@ def common():
|
||||
p = argparse.ArgumentParser(add_help=False)
|
||||
p.add_argument('--config', metavar='FILE', help='local build inputs (default: unixnu.local.toml in the UniXNU checkout)')
|
||||
p.add_argument('-D', '--device', metavar='DIR', help='device repository (remembered; or UNIXNU_DEVICE)')
|
||||
p.add_argument('--os', metavar='VERSION', help='OS profile for this run: 17, 26, 27 or a profiles/os directory name (default: the board\'s os_profile)')
|
||||
p.add_argument('--out', metavar='DIR', help='output root (default: out/ here, or UNIXNU_OUT)')
|
||||
p.add_argument('-j', '--jobs', type=int, help='parallel jobs')
|
||||
p.add_argument('--local', metavar='DIR', help='directory laid out like this repository; its drivers, kits, patches, scripts and package are used')
|
||||
|
||||
@@ -152,7 +152,8 @@ class Context:
|
||||
if not selected or Path(selected).name != selected or selected in ('.', '..'):
|
||||
raise Error('schema_version = 2 needs [kernel] defconfig or a selected defconfig')
|
||||
self.config_name = selected
|
||||
self.work /= self.board['board']['os_profile']
|
||||
from . import profiles
|
||||
self.work /= profiles.resolve_os(self, args.os) if getattr(args, 'os', None) else self.board['board']['os_profile']
|
||||
self.work /= selected.removesuffix('_defconfig')
|
||||
self.tree = self.work / 'tree'
|
||||
self.logs = self.work / 'logs'
|
||||
|
||||
@@ -16,10 +16,23 @@ def merge(base, overlay):
|
||||
return result
|
||||
|
||||
|
||||
def resolve_os(ctx, wanted):
|
||||
"""--os 17, 26 or 27 (the OS major version) or a full profiles/os directory name."""
|
||||
names = sorted(path.name for path in (ctx.root / 'profiles/os').iterdir() if path.is_dir())
|
||||
if wanted in names:
|
||||
return wanted
|
||||
matches = [name for name in names if name.startswith(f'ipados-{wanted}.')]
|
||||
if len(matches) != 1:
|
||||
raise Error(f'--os {wanted}: expected one of {", ".join(names)} or 17, 26, 27')
|
||||
return matches[0]
|
||||
|
||||
|
||||
def load(ctx, allow_fetch=False):
|
||||
from . import feeds
|
||||
board = ctx.raw_board.get('board', {})
|
||||
soc_name, os_name = board.get('soc_source'), board.get('os_profile')
|
||||
if getattr(ctx.args, 'os', None):
|
||||
os_name = resolve_os(ctx, ctx.args.os)
|
||||
if not soc_name or not os_name:
|
||||
raise Error('schema_version = 2 requires [board] soc_source and os_profile')
|
||||
if Path(os_name).name != os_name or os_name in ('.', '..'):
|
||||
|
||||
Reference in new issue
Block a user