1/*
2 * Copyright (C) 2022 Lima Project
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 */
7
8#include "drm-uapi/lima_drm.h"
9
10#include "util/u_math.h"
11#include "util/format/u_format.h"
12#include "util/u_surface.h"
13#include "util/u_inlines.h"
14#include "util/hash_table.h"
15
16#include "lima_context.h"
17#include "lima_gpu.h"
18#include "lima_resource.h"
19#include "lima_texture.h"
20#include "lima_format.h"
21#include "lima_job.h"
22#include "lima_screen.h"
23#include "lima_bo.h"
24#include "lima_parser.h"
25#include "lima_util.h"
26#include "lima_blit.h"
27
28void
29lima_pack_blit_cmd(struct lima_job *job,
30                   struct util_dynarray *cmd_array,
31                   struct pipe_surface *psurf,
32                   const struct pipe_box *src,
33                   const struct pipe_box *dst,
34                   unsigned filter,
35                   bool scissor,
36                   unsigned sample_mask,
37                   unsigned mrt_idx)
38{
39   #define lima_blit_render_state_offset 0x0000
40   #define lima_blit_gl_pos_offset       0x0040
41   #define lima_blit_varying_offset      0x0080
42   #define lima_blit_tex_desc_offset     0x00c0
43   #define lima_blit_tex_array_offset    0x0100
44   #define lima_blit_buffer_size         0x0140
45
46   struct lima_context *ctx = job->ctx;
47   struct lima_surface *surf = lima_surface(psurf);
48   int level = psurf->u.tex.level;
49   unsigned first_layer = psurf->u.tex.first_layer;
50   float fb_width = dst->width, fb_height = dst->height;
51
52   uint32_t va;
53   void *cpu = lima_job_create_stream_bo(
54      job, LIMA_PIPE_PP, lima_blit_buffer_size, &va);
55
56   struct lima_screen *screen = lima_screen(ctx->base.screen);
57
58   uint32_t reload_shader_first_instr_size =
59      ((uint32_t *)(screen->pp_buffer->map + pp_reload_program_offset))[0] & 0x1f;
60   uint32_t reload_shader_va = screen->pp_buffer->va + pp_reload_program_offset;
61
62   struct lima_render_state reload_render_state = {
63      .alpha_blend = 0xf03b1ad2,
64      .depth_test = 0x0000000e,
65      .depth_range = 0xffff0000,
66      .stencil_front = 0x00000007,
67      .stencil_back = 0x00000007,
68      .multi_sample = 0x00000007,
69      .shader_address = reload_shader_va | reload_shader_first_instr_size,
70      .varying_types = 0x00000001,
71      .textures_address = va + lima_blit_tex_array_offset,
72      .aux0 = 0x00004021,
73      .varyings_address = va + lima_blit_varying_offset,
74   };
75
76   reload_render_state.multi_sample |= (sample_mask << 12);
77
78   if (job->key.cbuf) {
79      fb_width = job->key.cbuf->width;
80      fb_height = job->key.cbuf->height;
81   } else {
82      fb_width = job->key.zsbuf->width;
83      fb_height = job->key.zsbuf->height;
84   }
85
86   if (util_format_is_depth_or_stencil(psurf->format)) {
87      reload_render_state.alpha_blend &= 0x0fffffff;
88      if (psurf->format != PIPE_FORMAT_Z16_UNORM)
89         reload_render_state.depth_test |= 0x400;
90      if (surf->reload & PIPE_CLEAR_DEPTH)
91         reload_render_state.depth_test |= 0x801;
92      if (surf->reload & PIPE_CLEAR_STENCIL) {
93         reload_render_state.depth_test |= 0x1000;
94         reload_render_state.stencil_front = 0x0000024f;
95         reload_render_state.stencil_back = 0x0000024f;
96         reload_render_state.stencil_test = 0x0000ffff;
97      }
98   }
99
100   memcpy(cpu + lima_blit_render_state_offset, &reload_render_state,
101          sizeof(reload_render_state));
102
103   lima_tex_desc *td = cpu + lima_blit_tex_desc_offset;
104   memset(td, 0, lima_min_tex_desc_size);
105   lima_texture_desc_set_res(ctx, td, psurf->texture, level, level,
106                             first_layer, mrt_idx);
107   td->format = lima_format_get_texel_reload(psurf->format);
108   td->unnorm_coords = 1;
109   td->sampler_dim = LIMA_SAMPLER_DIM_2D;
110   td->min_img_filter_nearest = 1;
111   td->mag_img_filter_nearest = 1;
112   td->wrap_s = LIMA_TEX_WRAP_CLAMP_TO_EDGE;
113   td->wrap_t = LIMA_TEX_WRAP_CLAMP_TO_EDGE;
114   td->wrap_r = LIMA_TEX_WRAP_CLAMP_TO_EDGE;
115
116   if (filter != PIPE_TEX_FILTER_NEAREST) {
117      td->min_img_filter_nearest = 0;
118      td->mag_img_filter_nearest = 0;
119   }
120
121   uint32_t *ta = cpu + lima_blit_tex_array_offset;
122   ta[0] = va + lima_blit_tex_desc_offset;
123
124   float reload_gl_pos[] = {
125      dst->x + dst->width, dst->y,      0, 1,
126      dst->x,              dst->y,      0, 1,
127      dst->x, dst->y + dst->height,     0, 1,
128   };
129   memcpy(cpu + lima_blit_gl_pos_offset, reload_gl_pos,
130          sizeof(reload_gl_pos));
131
132   float reload_varying[] = {
133      src->x + src->width, src->y,
134      src->x,              src->y,
135      src->x,              src->y + src->height,
136      0, 0, /* unused */
137   };
138   memcpy(cpu + lima_blit_varying_offset, reload_varying,
139          sizeof(reload_varying));
140
141   PLBU_CMD_BEGIN(cmd_array, scissor ? 22 : 20);
142
143   PLBU_CMD_VIEWPORT_LEFT(0);
144   PLBU_CMD_VIEWPORT_RIGHT(fui(fb_width));
145   PLBU_CMD_VIEWPORT_BOTTOM(0);
146   PLBU_CMD_VIEWPORT_TOP(fui(fb_height));
147
148   PLBU_CMD_RSW_VERTEX_ARRAY(
149      va + lima_blit_render_state_offset,
150      va + lima_blit_gl_pos_offset);
151
152
153   if (scissor) {
154      int minx = MIN2(dst->x, dst->x + dst->width);
155      int maxx = MAX2(dst->x, dst->x + dst->width);
156      int miny = MIN2(dst->y, dst->y + dst->height);
157      int maxy = MAX2(dst->y, dst->y + dst->height);
158
159      PLBU_CMD_SCISSORS(minx, maxx, miny, maxy);
160      lima_damage_rect_union(&job->damage_rect, minx, maxx, miny, maxy);
161   }
162
163   PLBU_CMD_UNKNOWN2();
164   PLBU_CMD_UNKNOWN1();
165
166   PLBU_CMD_INDICES(screen->pp_buffer->va + pp_shared_index_offset);
167   PLBU_CMD_INDEXED_DEST(va + lima_blit_gl_pos_offset);
168   PLBU_CMD_DRAW_ELEMENTS(0xf, 0, 3);
169
170   PLBU_CMD_END();
171
172   lima_dump_command_stream_print(job->dump, cpu, lima_blit_buffer_size,
173                                  false, "blit plbu cmd at va %x\n", va);
174}
175
176static struct pipe_surface *
177lima_get_blit_surface(struct pipe_context *pctx,
178                      struct pipe_resource *prsc,
179                      unsigned level)
180{
181   struct pipe_surface tmpl;
182
183   memset(&tmpl, 0, sizeof(tmpl));
184   tmpl.format = prsc->format;
185   tmpl.u.tex.level = level;
186   tmpl.u.tex.first_layer = 0;
187   tmpl.u.tex.last_layer = 0;
188
189   return pctx->create_surface(pctx, prsc, &tmpl);
190}
191
192bool
193lima_do_blit(struct pipe_context *pctx,
194             const struct pipe_blit_info *info)
195{
196   struct lima_context *ctx = lima_context(pctx);
197   unsigned reload_flags = PIPE_CLEAR_COLOR0;
198   uint8_t identity[4] = { PIPE_SWIZZLE_X, PIPE_SWIZZLE_Y,
199                           PIPE_SWIZZLE_Z, PIPE_SWIZZLE_W };
200
201   if (lima_debug & LIMA_DEBUG_NO_BLIT)
202      return false;
203
204   /* Blitting of swizzled formats (R and RG) isn't implemented yet */
205   if (memcmp(identity,
206              lima_format_get_texel_swizzle(info->src.resource->format),
207              sizeof(identity)))
208      return false;
209
210   if (memcmp(identity,
211              lima_format_get_texel_swizzle(info->dst.resource->format),
212              sizeof(identity)))
213      return false;
214
215   if (util_format_is_depth_or_stencil(info->src.resource->format)) {
216      const struct util_format_description *desc =
217         util_format_description(info->src.resource->format);
218      reload_flags = 0;
219      if (util_format_has_depth(desc))
220         reload_flags |= PIPE_CLEAR_DEPTH;
221      if (util_format_has_stencil(desc))
222         reload_flags |= PIPE_CLEAR_STENCIL;
223   }
224
225   if (!lima_format_pixel_supported(info->dst.resource->format))
226      return false;
227
228   if (!lima_format_texel_supported(info->src.resource->format))
229      return false;
230
231   if (info->dst.resource->target != PIPE_TEXTURE_2D ||
232       info->src.resource->target != PIPE_TEXTURE_2D)
233      return false;
234
235   if (info->dst.box.x < 0 || info->dst.box.y < 0 ||
236       info->src.box.x < 0 || info->src.box.y < 0)
237      return false;
238
239   if (info->src.box.depth != 1 ||
240       info->dst.box.depth != 1)
241      return false;
242
243   /* Scissored blit isn't implemented yet */
244   if (info->scissor_enable)
245      return false;
246
247   if ((reload_flags & PIPE_CLEAR_COLOR) && !(info->mask & PIPE_MASK_RGBA))
248      return false;
249
250   if ((reload_flags & PIPE_CLEAR_DEPTH) && !(info->mask & PIPE_MASK_Z))
251      return false;
252
253   if ((reload_flags & PIPE_CLEAR_STENCIL) && !(info->mask & PIPE_MASK_S))
254      return false;
255
256   struct pipe_surface *dst_surf =
257         lima_get_blit_surface(pctx, info->dst.resource, info->dst.level);
258   struct lima_surface *lima_dst_surf = lima_surface(dst_surf);
259
260   struct pipe_surface *src_surf =
261         lima_get_blit_surface(pctx, info->src.resource, info->src.level);
262
263   struct lima_job *job;
264
265   if (util_format_is_depth_or_stencil(info->dst.resource->format))
266      job = lima_job_get_with_fb(ctx, NULL, dst_surf);
267   else
268      job = lima_job_get_with_fb(ctx, dst_surf, NULL);
269
270   struct lima_resource *src_res = lima_resource(src_surf->texture);
271   struct lima_resource *dst_res = lima_resource(dst_surf->texture);
272
273   lima_flush_job_accessing_bo(ctx, src_res->bo, true);
274   lima_flush_job_accessing_bo(ctx, dst_res->bo, true);
275
276   lima_job_add_bo(job, LIMA_PIPE_PP, src_res->bo, LIMA_SUBMIT_BO_READ);
277   _mesa_hash_table_insert(ctx->write_jobs, &dst_res->base, job);
278   lima_job_add_bo(job, LIMA_PIPE_PP, dst_res->bo, LIMA_SUBMIT_BO_WRITE);
279
280   if (info->src.resource->nr_samples > 1) {
281      for (int i = 0; i < MIN2(info->src.resource->nr_samples, LIMA_MAX_SAMPLES); i++) {
282         lima_pack_blit_cmd(job, &job->plbu_cmd_array,
283                            src_surf, &info->src.box,
284                            &info->dst.box, info->filter, true,
285                            1 << i, i);
286      }
287   } else {
288      lima_pack_blit_cmd(job, &job->plbu_cmd_array,
289                         src_surf, &info->src.box,
290                         &info->dst.box, info->filter, true,
291                         0xf, 0);
292   }
293
294   bool tile_aligned = false;
295
296   if (info->dst.box.x == 0 && info->dst.box.y == 0 &&
297       info->dst.box.width == lima_dst_surf->base.width &&
298       info->dst.box.height == lima_dst_surf->base.height)
299      tile_aligned = true;
300
301   if (info->dst.box.x % 16 == 0 && info->dst.box.y % 16 == 0 &&
302       info->dst.box.width % 16 == 0 && info->dst.box.height % 16 == 0)
303      tile_aligned = true;
304
305   /* Reload if dest is not aligned to tile boundaries */
306   if (!tile_aligned)
307      lima_dst_surf->reload = reload_flags;
308   else
309      lima_dst_surf->reload = 0;
310
311   job->resolve = reload_flags;
312
313   lima_do_job(job);
314
315   pipe_surface_reference(&dst_surf, NULL);
316   pipe_surface_reference(&src_surf, NULL);
317
318   return true;
319}
320