1/*
2 * Copyright 2019 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include "isl/isl.h"
25
26#ifdef IN_UNIT_TEST
27/* STATIC_ASSERT is a do { ... } while(0) statement */
28UNUSED static void static_assert_func(void) {
29   STATIC_ASSERT(ISL_AUX_OP_ASSERT == ((enum isl_aux_op) 0));
30   STATIC_ASSERT(ISL_AUX_STATE_ASSERT == ((enum isl_aux_state) 0));
31}
32
33#undef unreachable
34#define unreachable(str) return 0
35
36#undef assert
37#define assert(cond) do { \
38   if (!(cond)) { \
39      return 0; \
40   } \
41} while (0)
42#endif
43
44/* How writes with an isl_aux_usage behave. */
45enum write_behavior {
46   /* Writes only touch the main surface. */
47   WRITES_ONLY_TOUCH_MAIN = 0,
48
49   /* Writes using the 3D engine are compressed. */
50   WRITES_COMPRESS,
51
52   /* Writes using the 3D engine are either compressed or substituted with
53    * fast-cleared blocks.
54    */
55   WRITES_COMPRESS_CLEAR,
56
57   /* Writes implicitly fully resolve the compression block and write the data
58    * uncompressed into the main surface. The resolved aux blocks are
59    * ambiguated and left in the pass-through state.
60    */
61   WRITES_RESOLVE_AMBIGUATE,
62};
63
64/* A set of features supported by an isl_aux_usage. */
65struct aux_usage_info {
66
67   /* How writes affect the surface(s) in use. */
68   enum write_behavior write_behavior;
69
70   /* Aux supports "real" compression beyond just fast-clears. */
71   bool compressed;
72
73   /* SW can perform ISL_AUX_OP_FAST_CLEAR. */
74   bool fast_clear;
75
76   /* SW can perform ISL_AUX_OP_PARTIAL_RESOLVE. */
77   bool partial_resolve;
78
79   /* Performing ISL_AUX_OP_FULL_RESOLVE includes ISL_AUX_OP_AMBIGUATE. */
80   bool full_resolves_ambiguate;
81};
82
83#define AUX(wb, c, fc, pr, fra, type)                   \
84   [ISL_AUX_USAGE_ ## type] = { WRITES_ ## wb, c, fc, pr, fra},
85#define Y true
86#define x false
87static const struct aux_usage_info info[] = {
88/*         write_behavior c fc pr fra */
89   AUX(         COMPRESS, Y, Y, x, x, HIZ)
90   AUX(         COMPRESS, Y, Y, x, x, HIZ_CCS)
91   AUX(         COMPRESS, Y, Y, x, x, HIZ_CCS_WT)
92   AUX(         COMPRESS, Y, Y, Y, x, MCS)
93   AUX(         COMPRESS, Y, Y, Y, x, MCS_CCS)
94   AUX(         COMPRESS, Y, Y, Y, Y, CCS_E)
95   AUX(   COMPRESS_CLEAR, Y, Y, Y, Y, GFX12_CCS_E)
96   AUX(RESOLVE_AMBIGUATE, x, Y, x, Y, CCS_D)
97   AUX(RESOLVE_AMBIGUATE, Y, x, x, Y, MC)
98   AUX(         COMPRESS, Y, x, x, Y, STC_CCS)
99};
100#undef x
101#undef Y
102#undef AUX
103
104ASSERTED static bool
105aux_state_possible(enum isl_aux_state state,
106                   enum isl_aux_usage usage)
107{
108   switch (state) {
109   case ISL_AUX_STATE_CLEAR:
110   case ISL_AUX_STATE_PARTIAL_CLEAR:
111      return info[usage].fast_clear;
112   case ISL_AUX_STATE_COMPRESSED_CLEAR:
113      return info[usage].fast_clear && info[usage].compressed;
114   case ISL_AUX_STATE_COMPRESSED_NO_CLEAR:
115      return info[usage].compressed;
116   case ISL_AUX_STATE_RESOLVED:
117   case ISL_AUX_STATE_PASS_THROUGH:
118   case ISL_AUX_STATE_AUX_INVALID:
119      return true;
120#ifdef IN_UNIT_TEST
121   case ISL_AUX_STATE_ASSERT:
122      break;
123#endif
124   }
125
126   unreachable("Invalid aux state.");
127}
128
129enum isl_aux_op
130isl_aux_prepare_access(enum isl_aux_state initial_state,
131                       enum isl_aux_usage usage,
132                       bool fast_clear_supported)
133{
134   if (usage != ISL_AUX_USAGE_NONE) {
135      UNUSED const enum isl_aux_usage state_superset_usage =
136         usage == ISL_AUX_USAGE_CCS_D ? ISL_AUX_USAGE_CCS_E : usage;
137      assert(aux_state_possible(initial_state, state_superset_usage));
138   }
139   assert(!fast_clear_supported || info[usage].fast_clear);
140
141   switch (initial_state) {
142   case ISL_AUX_STATE_COMPRESSED_CLEAR:
143      if (!info[usage].compressed)
144         return ISL_AUX_OP_FULL_RESOLVE;
145      FALLTHROUGH;
146   case ISL_AUX_STATE_CLEAR:
147   case ISL_AUX_STATE_PARTIAL_CLEAR:
148      return fast_clear_supported ?
149                ISL_AUX_OP_NONE :
150             info[usage].partial_resolve ?
151                ISL_AUX_OP_PARTIAL_RESOLVE : ISL_AUX_OP_FULL_RESOLVE;
152   case ISL_AUX_STATE_COMPRESSED_NO_CLEAR:
153      return info[usage].compressed ?
154             ISL_AUX_OP_NONE : ISL_AUX_OP_FULL_RESOLVE;
155   case ISL_AUX_STATE_RESOLVED:
156   case ISL_AUX_STATE_PASS_THROUGH:
157      return ISL_AUX_OP_NONE;
158   case ISL_AUX_STATE_AUX_INVALID:
159      return info[usage].write_behavior == WRITES_ONLY_TOUCH_MAIN ?
160             ISL_AUX_OP_NONE : ISL_AUX_OP_AMBIGUATE;
161#ifdef IN_UNIT_TEST
162   case ISL_AUX_STATE_ASSERT:
163      break;
164#endif
165   }
166
167   unreachable("Invalid aux state.");
168}
169
170enum isl_aux_state
171isl_aux_state_transition_aux_op(enum isl_aux_state initial_state,
172                                enum isl_aux_usage usage,
173                                enum isl_aux_op op)
174{
175   assert(aux_state_possible(initial_state, usage));
176   assert(usage != ISL_AUX_USAGE_NONE || op == ISL_AUX_OP_NONE);
177
178   switch (op) {
179   case ISL_AUX_OP_NONE:
180      return initial_state;
181   case ISL_AUX_OP_FAST_CLEAR:
182      assert(info[usage].fast_clear);
183      return ISL_AUX_STATE_CLEAR;
184   case ISL_AUX_OP_PARTIAL_RESOLVE:
185      assert(isl_aux_state_has_valid_aux(initial_state));
186      assert(info[usage].partial_resolve);
187      return initial_state == ISL_AUX_STATE_CLEAR ||
188             initial_state == ISL_AUX_STATE_PARTIAL_CLEAR ||
189             initial_state == ISL_AUX_STATE_COMPRESSED_CLEAR ?
190             ISL_AUX_STATE_COMPRESSED_NO_CLEAR : initial_state;
191   case ISL_AUX_OP_FULL_RESOLVE:
192      assert(isl_aux_state_has_valid_aux(initial_state));
193      return info[usage].full_resolves_ambiguate ||
194             initial_state == ISL_AUX_STATE_PASS_THROUGH ?
195             ISL_AUX_STATE_PASS_THROUGH : ISL_AUX_STATE_RESOLVED;
196   case ISL_AUX_OP_AMBIGUATE:
197      return ISL_AUX_STATE_PASS_THROUGH;
198#if IN_UNIT_TEST
199   case ISL_AUX_OP_ASSERT:
200      break;
201#endif
202   }
203
204   unreachable("Invalid aux op.");
205}
206
207enum isl_aux_state
208isl_aux_state_transition_write(enum isl_aux_state initial_state,
209                               enum isl_aux_usage usage,
210                               bool full_surface)
211{
212   if (info[usage].write_behavior == WRITES_ONLY_TOUCH_MAIN) {
213      assert(full_surface || isl_aux_state_has_valid_primary(initial_state));
214
215      return initial_state == ISL_AUX_STATE_PASS_THROUGH ?
216             ISL_AUX_STATE_PASS_THROUGH : ISL_AUX_STATE_AUX_INVALID;
217   }
218
219   assert(isl_aux_state_has_valid_aux(initial_state));
220   assert(aux_state_possible(initial_state, usage));
221   assert(info[usage].write_behavior == WRITES_COMPRESS ||
222          info[usage].write_behavior == WRITES_COMPRESS_CLEAR ||
223          info[usage].write_behavior == WRITES_RESOLVE_AMBIGUATE);
224
225   if (full_surface) {
226      return info[usage].write_behavior == WRITES_COMPRESS ?
227                ISL_AUX_STATE_COMPRESSED_NO_CLEAR :
228             info[usage].write_behavior == WRITES_COMPRESS_CLEAR ?
229                ISL_AUX_STATE_COMPRESSED_CLEAR : ISL_AUX_STATE_PASS_THROUGH;
230   }
231
232   switch (initial_state) {
233   case ISL_AUX_STATE_CLEAR:
234   case ISL_AUX_STATE_PARTIAL_CLEAR:
235      return info[usage].write_behavior == WRITES_RESOLVE_AMBIGUATE ?
236             ISL_AUX_STATE_PARTIAL_CLEAR : ISL_AUX_STATE_COMPRESSED_CLEAR;
237   case ISL_AUX_STATE_RESOLVED:
238   case ISL_AUX_STATE_PASS_THROUGH:
239   case ISL_AUX_STATE_COMPRESSED_NO_CLEAR:
240      return info[usage].write_behavior == WRITES_COMPRESS ?
241                ISL_AUX_STATE_COMPRESSED_NO_CLEAR :
242             info[usage].write_behavior == WRITES_COMPRESS_CLEAR ?
243                ISL_AUX_STATE_COMPRESSED_CLEAR : initial_state;
244   case ISL_AUX_STATE_COMPRESSED_CLEAR:
245   case ISL_AUX_STATE_AUX_INVALID:
246      return initial_state;
247#ifdef IN_UNIT_TEST
248   case ISL_AUX_STATE_ASSERT:
249      break;
250#endif
251   }
252
253   unreachable("Invalid aux state.");
254}
255
256bool
257isl_aux_usage_has_fast_clears(enum isl_aux_usage usage)
258{
259   return info[usage].fast_clear;
260}
261
262bool
263isl_aux_usage_has_compression(enum isl_aux_usage usage)
264{
265   return info[usage].compressed;
266}
267