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:
rpone authored and Sharky Wang committed 2026-10-30 23:54:41 +08:00
1 parent a7c92c8cc6
commit 4b57b67251
9 files changed
+557 -5

No files matched your search

+488
View File
@@ -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;
+2 -1
View File
@@ -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",
+48
View File
@@ -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')
+1
View File
@@ -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')
+2 -1
View File
@@ -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'
+13
View File
@@ -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 ('.', '..'):