tgsi_dump.c revision bdbe77f9c6f06cfaa155f27c2ade3c523d7fbea7
1/**************************************************************************
2 *
3 * Copyright 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "util/u_debug.h"
29#include "util/u_string.h"
30#include "util/u_math.h"
31#include "util/u_memory.h"
32#include "tgsi_dump.h"
33#include "tgsi_info.h"
34#include "tgsi_iterate.h"
35
36
37/** Number of spaces to indent for IF/LOOP/etc */
38static const int indent_spaces = 3;
39
40
41struct dump_ctx
42{
43   struct tgsi_iterate_context iter;
44
45   uint instno;
46   int indent;
47
48   uint indentation;
49
50   void (*printf)(struct dump_ctx *ctx, const char *format, ...);
51};
52
53static void
54dump_ctx_printf(struct dump_ctx *ctx, const char *format, ...)
55{
56   va_list ap;
57   (void)ctx;
58   va_start(ap, format);
59   _debug_vprintf(format, ap);
60   va_end(ap);
61}
62
63static void
64dump_enum(
65   struct dump_ctx *ctx,
66   uint e,
67   const char **enums,
68   uint enum_count )
69{
70   if (e >= enum_count)
71      ctx->printf( ctx, "%u", e );
72   else
73      ctx->printf( ctx, "%s", enums[e] );
74}
75
76#define EOL()           ctx->printf( ctx, "\n" )
77#define TXT(S)          ctx->printf( ctx, "%s", S )
78#define CHR(C)          ctx->printf( ctx, "%c", C )
79#define UIX(I)          ctx->printf( ctx, "0x%x", I )
80#define UID(I)          ctx->printf( ctx, "%u", I )
81#define INSTID(I)          ctx->printf( ctx, "% 3u", I )
82#define SID(I)          ctx->printf( ctx, "%d", I )
83#define FLT(F)          ctx->printf( ctx, "%10.4f", F )
84#define ENM(E,ENUMS)    dump_enum( ctx, E, ENUMS, sizeof( ENUMS ) / sizeof( *ENUMS ) )
85
86static const char *processor_type_names[] =
87{
88   "FRAG",
89   "VERT",
90   "GEOM"
91};
92
93const char *
94tgsi_file_names[TGSI_FILE_COUNT] =
95{
96   "NULL",
97   "CONST",
98   "IN",
99   "OUT",
100   "TEMP",
101   "SAMP",
102   "ADDR",
103   "IMM",
104   "PRED",
105   "SV",
106   "IMMX",
107   "TEMPX",
108   "RES"
109};
110
111static const char *interpolate_names[] =
112{
113   "CONSTANT",
114   "LINEAR",
115   "PERSPECTIVE"
116};
117
118static const char *semantic_names[] =
119{
120   "POSITION",
121   "COLOR",
122   "BCOLOR",
123   "FOG",
124   "PSIZE",
125   "GENERIC",
126   "NORMAL",
127   "FACE",
128   "EDGEFLAG",
129   "PRIM_ID",
130   "INSTANCEID",
131   "STENCIL"
132};
133
134static const char *immediate_type_names[] =
135{
136   "FLT32",
137   "UINT32",
138   "INT32"
139};
140
141const char *
142tgsi_swizzle_names[4] =
143{
144   "x",
145   "y",
146   "z",
147   "w"
148};
149
150const char *
151tgsi_texture_names[TGSI_TEXTURE_COUNT] =
152{
153   "UNKNOWN",
154   "1D",
155   "2D",
156   "3D",
157   "CUBE",
158   "RECT",
159   "SHADOW1D",
160   "SHADOW2D",
161   "SHADOWRECT"
162};
163
164const char *tgsi_property_names[TGSI_PROPERTY_COUNT] =
165{
166   "GS_INPUT_PRIMITIVE",
167   "GS_OUTPUT_PRIMITIVE",
168   "GS_MAX_OUTPUT_VERTICES",
169   "FS_COORD_ORIGIN",
170   "FS_COORD_PIXEL_CENTER",
171   "FS_COLOR0_WRITES_ALL_CBUFS",
172};
173
174static const char *tgsi_type_names[] =
175{
176   "UNORM",
177   "SNORM",
178   "SINT",
179   "UINT",
180   "FLOAT"
181};
182
183const char *tgsi_primitive_names[PIPE_PRIM_MAX] =
184{
185   "POINTS",
186   "LINES",
187   "LINE_LOOP",
188   "LINE_STRIP",
189   "TRIANGLES",
190   "TRIANGLE_STRIP",
191   "TRIANGLE_FAN",
192   "QUADS",
193   "QUAD_STRIP",
194   "POLYGON",
195   "LINES_ADJACENCY",
196   "LINE_STRIP_ADJACENCY",
197   "TRIANGLES_ADJACENCY",
198   "TRIANGLE_STRIP_ADJACENCY"
199};
200
201const char *tgsi_fs_coord_origin_names[2] =
202{
203   "UPPER_LEFT",
204   "LOWER_LEFT"
205};
206
207const char *tgsi_fs_coord_pixel_center_names[2] =
208{
209   "HALF_INTEGER",
210   "INTEGER"
211};
212
213
214static void
215_dump_register_src(
216   struct dump_ctx *ctx,
217   const struct tgsi_full_src_register *src )
218{
219   ENM(src->Register.File, tgsi_file_names);
220   if (src->Register.Dimension) {
221      if (src->Dimension.Indirect) {
222         CHR( '[' );
223         ENM( src->DimIndirect.File, tgsi_file_names );
224         CHR( '[' );
225         SID( src->DimIndirect.Index );
226         TXT( "]." );
227         ENM( src->DimIndirect.SwizzleX, tgsi_swizzle_names );
228         if (src->Dimension.Index != 0) {
229            if (src->Dimension.Index > 0)
230               CHR( '+' );
231            SID( src->Dimension.Index );
232         }
233         CHR( ']' );
234      } else {
235         CHR('[');
236         SID(src->Dimension.Index);
237         CHR(']');
238      }
239   }
240   if (src->Register.Indirect) {
241      CHR( '[' );
242      ENM( src->Indirect.File, tgsi_file_names );
243      CHR( '[' );
244      SID( src->Indirect.Index );
245      TXT( "]." );
246      ENM( src->Indirect.SwizzleX, tgsi_swizzle_names );
247      if (src->Register.Index != 0) {
248         if (src->Register.Index > 0)
249            CHR( '+' );
250         SID( src->Register.Index );
251      }
252      CHR( ']' );
253   } else {
254      CHR( '[' );
255      SID( src->Register.Index );
256      CHR( ']' );
257   }
258}
259
260
261static void
262_dump_register_dst(
263   struct dump_ctx *ctx,
264   const struct tgsi_full_dst_register *dst )
265{
266   ENM(dst->Register.File, tgsi_file_names);
267   if (dst->Register.Dimension) {
268      if (dst->Dimension.Indirect) {
269         CHR( '[' );
270         ENM( dst->DimIndirect.File, tgsi_file_names );
271         CHR( '[' );
272         SID( dst->DimIndirect.Index );
273         TXT( "]." );
274         ENM( dst->DimIndirect.SwizzleX, tgsi_swizzle_names );
275         if (dst->Dimension.Index != 0) {
276            if (dst->Dimension.Index > 0)
277               CHR( '+' );
278            SID( dst->Dimension.Index );
279         }
280         CHR( ']' );
281      } else {
282         CHR('[');
283         SID(dst->Dimension.Index);
284         CHR(']');
285      }
286   }
287   if (dst->Register.Indirect) {
288      CHR( '[' );
289      ENM( dst->Indirect.File, tgsi_file_names );
290      CHR( '[' );
291      SID( dst->Indirect.Index );
292      TXT( "]." );
293      ENM( dst->Indirect.SwizzleX, tgsi_swizzle_names );
294      if (dst->Register.Index != 0) {
295         if (dst->Register.Index > 0)
296            CHR( '+' );
297         SID( dst->Register.Index );
298      }
299      CHR( ']' );
300   } else {
301      CHR( '[' );
302      SID( dst->Register.Index );
303      CHR( ']' );
304   }
305}
306static void
307_dump_writemask(
308   struct dump_ctx *ctx,
309   uint writemask )
310{
311   if (writemask != TGSI_WRITEMASK_XYZW) {
312      CHR( '.' );
313      if (writemask & TGSI_WRITEMASK_X)
314         CHR( 'x' );
315      if (writemask & TGSI_WRITEMASK_Y)
316         CHR( 'y' );
317      if (writemask & TGSI_WRITEMASK_Z)
318         CHR( 'z' );
319      if (writemask & TGSI_WRITEMASK_W)
320         CHR( 'w' );
321   }
322}
323
324static void
325dump_imm_data(struct tgsi_iterate_context *iter,
326              union tgsi_immediate_data *data,
327              unsigned num_tokens,
328              unsigned data_type)
329{
330   struct dump_ctx *ctx = (struct dump_ctx *)iter;
331   unsigned i ;
332
333   TXT( " {" );
334
335   assert( num_tokens <= 4 );
336   for (i = 0; i < num_tokens; i++) {
337      switch (data_type) {
338      case TGSI_IMM_FLOAT32:
339         FLT( data[i].Float );
340         break;
341      case TGSI_IMM_UINT32:
342         UID(data[i].Uint);
343         break;
344      case TGSI_IMM_INT32:
345         SID(data[i].Int);
346         break;
347      default:
348         assert( 0 );
349      }
350
351      if (i < num_tokens - 1)
352         TXT( ", " );
353   }
354   TXT( "}" );
355}
356
357static boolean
358iter_declaration(
359   struct tgsi_iterate_context *iter,
360   struct tgsi_full_declaration *decl )
361{
362   struct dump_ctx *ctx = (struct dump_ctx *)iter;
363
364   assert(Elements(semantic_names) == TGSI_SEMANTIC_COUNT);
365   assert(Elements(interpolate_names) == TGSI_INTERPOLATE_COUNT);
366
367   TXT( "DCL " );
368
369   ENM(decl->Declaration.File, tgsi_file_names);
370
371   /* all geometry shader inputs are two dimensional */
372   if (decl->Declaration.File == TGSI_FILE_INPUT &&
373       iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY) {
374      TXT("[]");
375   }
376
377   if (decl->Declaration.Dimension) {
378      CHR('[');
379      SID(decl->Dim.Index2D);
380      CHR(']');
381   }
382
383   CHR('[');
384   SID(decl->Range.First);
385   if (decl->Range.First != decl->Range.Last) {
386      TXT("..");
387      SID(decl->Range.Last);
388   }
389   CHR(']');
390
391   _dump_writemask(
392      ctx,
393      decl->Declaration.UsageMask );
394
395   if (decl->Declaration.Semantic) {
396      TXT( ", " );
397      ENM( decl->Semantic.Name, semantic_names );
398      if (decl->Semantic.Index != 0 ||
399          decl->Semantic.Name == TGSI_SEMANTIC_GENERIC) {
400         CHR( '[' );
401         UID( decl->Semantic.Index );
402         CHR( ']' );
403      }
404   }
405
406   if (decl->Declaration.File == TGSI_FILE_RESOURCE) {
407      TXT(", ");
408      ENM(decl->Resource.Resource, tgsi_texture_names);
409      TXT(", ");
410      if ((decl->Resource.ReturnTypeX == decl->Resource.ReturnTypeY) &&
411          (decl->Resource.ReturnTypeX == decl->Resource.ReturnTypeZ) &&
412          (decl->Resource.ReturnTypeX == decl->Resource.ReturnTypeW)) {
413         ENM(decl->Resource.ReturnTypeX, tgsi_type_names);
414      } else {
415         ENM(decl->Resource.ReturnTypeX, tgsi_type_names);
416         TXT(", ");
417         ENM(decl->Resource.ReturnTypeY, tgsi_type_names);
418         TXT(", ");
419         ENM(decl->Resource.ReturnTypeZ, tgsi_type_names);
420         TXT(", ");
421         ENM(decl->Resource.ReturnTypeW, tgsi_type_names);
422      }
423
424   }
425
426   if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT &&
427       decl->Declaration.File == TGSI_FILE_INPUT)
428   {
429      TXT( ", " );
430      ENM( decl->Declaration.Interpolate, interpolate_names );
431   }
432
433   if (decl->Declaration.Centroid) {
434      TXT( ", CENTROID" );
435   }
436
437   if (decl->Declaration.Invariant) {
438      TXT( ", INVARIANT" );
439   }
440
441   if (decl->Declaration.CylindricalWrap) {
442      TXT(", CYLWRAP_");
443      if (decl->Declaration.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_X) {
444         CHR('X');
445      }
446      if (decl->Declaration.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_Y) {
447         CHR('Y');
448      }
449      if (decl->Declaration.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_Z) {
450         CHR('Z');
451      }
452      if (decl->Declaration.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_W) {
453         CHR('W');
454      }
455   }
456
457   if (decl->Declaration.File == TGSI_FILE_IMMEDIATE_ARRAY) {
458      unsigned i;
459      char range_indent[4];
460
461      TXT(" {");
462
463      if (decl->Range.Last < 10)
464         range_indent[0] = '\0';
465      else if (decl->Range.Last < 100) {
466         range_indent[0] = ' ';
467         range_indent[1] = '\0';
468      } else if (decl->Range.Last < 1000) {
469         range_indent[0] = ' ';
470         range_indent[1] = ' ';
471         range_indent[2] = '\0';
472      } else {
473         range_indent[0] = ' ';
474         range_indent[1] = ' ';
475         range_indent[2] = ' ';
476         range_indent[3] = '\0';
477      }
478
479      dump_imm_data(iter, decl->ImmediateData.u,
480                    4, TGSI_IMM_FLOAT32);
481      for(i = 1; i <= decl->Range.Last; ++i) {
482         /* indent by strlen of:
483          *   "DCL IMMX[0..1] {" */
484         CHR('\n');
485         TXT( "                " );
486         TXT( range_indent );
487         dump_imm_data(iter, decl->ImmediateData.u + i,
488                       4, TGSI_IMM_FLOAT32);
489      }
490
491      TXT(" }");
492   }
493
494   EOL();
495
496   return TRUE;
497}
498
499void
500tgsi_dump_declaration(
501   const struct tgsi_full_declaration *decl )
502{
503   struct dump_ctx ctx;
504
505   ctx.printf = dump_ctx_printf;
506
507   iter_declaration( &ctx.iter, (struct tgsi_full_declaration *)decl );
508}
509
510static boolean
511iter_property(
512   struct tgsi_iterate_context *iter,
513   struct tgsi_full_property *prop )
514{
515   int i;
516   struct dump_ctx *ctx = (struct dump_ctx *)iter;
517
518   assert(Elements(tgsi_property_names) == TGSI_PROPERTY_COUNT);
519
520   TXT( "PROPERTY " );
521   ENM(prop->Property.PropertyName, tgsi_property_names);
522
523   if (prop->Property.NrTokens > 1)
524      TXT(" ");
525
526   for (i = 0; i < prop->Property.NrTokens - 1; ++i) {
527      switch (prop->Property.PropertyName) {
528      case TGSI_PROPERTY_GS_INPUT_PRIM:
529      case TGSI_PROPERTY_GS_OUTPUT_PRIM:
530         ENM(prop->u[i].Data, tgsi_primitive_names);
531         break;
532      case TGSI_PROPERTY_FS_COORD_ORIGIN:
533         ENM(prop->u[i].Data, tgsi_fs_coord_origin_names);
534         break;
535      case TGSI_PROPERTY_FS_COORD_PIXEL_CENTER:
536         ENM(prop->u[i].Data, tgsi_fs_coord_pixel_center_names);
537         break;
538      default:
539         SID( prop->u[i].Data );
540         break;
541      }
542      if (i < prop->Property.NrTokens - 2)
543         TXT( ", " );
544   }
545   EOL();
546
547   return TRUE;
548}
549
550void tgsi_dump_property(
551   const struct tgsi_full_property *prop )
552{
553   struct dump_ctx ctx;
554
555   ctx.printf = dump_ctx_printf;
556
557   iter_property( &ctx.iter, (struct tgsi_full_property *)prop );
558}
559
560static boolean
561iter_immediate(
562   struct tgsi_iterate_context *iter,
563   struct tgsi_full_immediate *imm )
564{
565   struct dump_ctx *ctx = (struct dump_ctx *) iter;
566
567   TXT( "IMM " );
568   ENM( imm->Immediate.DataType, immediate_type_names );
569
570   dump_imm_data(iter, imm->u, imm->Immediate.NrTokens - 1,
571                 imm->Immediate.DataType);
572
573   EOL();
574
575   return TRUE;
576}
577
578void
579tgsi_dump_immediate(
580   const struct tgsi_full_immediate *imm )
581{
582   struct dump_ctx ctx;
583
584   ctx.printf = dump_ctx_printf;
585
586   iter_immediate( &ctx.iter, (struct tgsi_full_immediate *)imm );
587}
588
589static boolean
590iter_instruction(
591   struct tgsi_iterate_context *iter,
592   struct tgsi_full_instruction *inst )
593{
594   struct dump_ctx *ctx = (struct dump_ctx *) iter;
595   uint instno = ctx->instno++;
596   const struct tgsi_opcode_info *info = tgsi_get_opcode_info( inst->Instruction.Opcode );
597   uint i;
598   boolean first_reg = TRUE;
599
600   INSTID( instno );
601   TXT( ": " );
602
603   ctx->indent -= info->pre_dedent;
604   for(i = 0; (int)i < ctx->indent; ++i)
605      TXT( "  " );
606   ctx->indent += info->post_indent;
607
608   if (inst->Instruction.Predicate) {
609      CHR( '(' );
610
611      if (inst->Predicate.Negate)
612         CHR( '!' );
613
614      TXT( "PRED[" );
615      SID( inst->Predicate.Index );
616      CHR( ']' );
617
618      if (inst->Predicate.SwizzleX != TGSI_SWIZZLE_X ||
619          inst->Predicate.SwizzleY != TGSI_SWIZZLE_Y ||
620          inst->Predicate.SwizzleZ != TGSI_SWIZZLE_Z ||
621          inst->Predicate.SwizzleW != TGSI_SWIZZLE_W) {
622         CHR( '.' );
623         ENM( inst->Predicate.SwizzleX, tgsi_swizzle_names );
624         ENM( inst->Predicate.SwizzleY, tgsi_swizzle_names );
625         ENM( inst->Predicate.SwizzleZ, tgsi_swizzle_names );
626         ENM( inst->Predicate.SwizzleW, tgsi_swizzle_names );
627      }
628
629      TXT( ") " );
630   }
631
632   TXT( info->mnemonic );
633
634   switch (inst->Instruction.Saturate) {
635   case TGSI_SAT_NONE:
636      break;
637   case TGSI_SAT_ZERO_ONE:
638      TXT( "_SAT" );
639      break;
640   case TGSI_SAT_MINUS_PLUS_ONE:
641      TXT( "_SATNV" );
642      break;
643   default:
644      assert( 0 );
645   }
646
647   for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
648      const struct tgsi_full_dst_register *dst = &inst->Dst[i];
649
650      if (!first_reg)
651         CHR( ',' );
652      CHR( ' ' );
653
654      _dump_register_dst( ctx, dst );
655      _dump_writemask( ctx, dst->Register.WriteMask );
656
657      first_reg = FALSE;
658   }
659
660   for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
661      const struct tgsi_full_src_register *src = &inst->Src[i];
662
663      if (!first_reg)
664         CHR( ',' );
665      CHR( ' ' );
666
667      if (src->Register.Negate)
668         CHR( '-' );
669      if (src->Register.Absolute)
670         CHR( '|' );
671
672      _dump_register_src(ctx, src);
673
674      if (src->Register.SwizzleX != TGSI_SWIZZLE_X ||
675          src->Register.SwizzleY != TGSI_SWIZZLE_Y ||
676          src->Register.SwizzleZ != TGSI_SWIZZLE_Z ||
677          src->Register.SwizzleW != TGSI_SWIZZLE_W) {
678         CHR( '.' );
679         ENM( src->Register.SwizzleX, tgsi_swizzle_names );
680         ENM( src->Register.SwizzleY, tgsi_swizzle_names );
681         ENM( src->Register.SwizzleZ, tgsi_swizzle_names );
682         ENM( src->Register.SwizzleW, tgsi_swizzle_names );
683      }
684
685      if (src->Register.Absolute)
686         CHR( '|' );
687
688      first_reg = FALSE;
689   }
690
691   if (inst->Instruction.Texture) {
692      TXT( ", " );
693      ENM( inst->Texture.Texture, tgsi_texture_names );
694   }
695
696   switch (inst->Instruction.Opcode) {
697   case TGSI_OPCODE_IF:
698   case TGSI_OPCODE_ELSE:
699   case TGSI_OPCODE_BGNLOOP:
700   case TGSI_OPCODE_ENDLOOP:
701   case TGSI_OPCODE_CAL:
702      TXT( " :" );
703      UID( inst->Label.Label );
704      break;
705   }
706
707   /* update indentation */
708   if (inst->Instruction.Opcode == TGSI_OPCODE_IF ||
709       inst->Instruction.Opcode == TGSI_OPCODE_ELSE ||
710       inst->Instruction.Opcode == TGSI_OPCODE_BGNLOOP) {
711      ctx->indentation += indent_spaces;
712   }
713
714   EOL();
715
716   return TRUE;
717}
718
719void
720tgsi_dump_instruction(
721   const struct tgsi_full_instruction *inst,
722   uint instno )
723{
724   struct dump_ctx ctx;
725
726   ctx.instno = instno;
727   ctx.indent = 0;
728   ctx.printf = dump_ctx_printf;
729   ctx.indentation = 0;
730
731   iter_instruction( &ctx.iter, (struct tgsi_full_instruction *)inst );
732}
733
734static boolean
735prolog(
736   struct tgsi_iterate_context *iter )
737{
738   struct dump_ctx *ctx = (struct dump_ctx *) iter;
739   ENM( iter->processor.Processor, processor_type_names );
740   EOL();
741   return TRUE;
742}
743
744void
745tgsi_dump(
746   const struct tgsi_token *tokens,
747   uint flags )
748{
749   struct dump_ctx ctx;
750
751   ctx.iter.prolog = prolog;
752   ctx.iter.iterate_instruction = iter_instruction;
753   ctx.iter.iterate_declaration = iter_declaration;
754   ctx.iter.iterate_immediate = iter_immediate;
755   ctx.iter.iterate_property = iter_property;
756   ctx.iter.epilog = NULL;
757
758   ctx.instno = 0;
759   ctx.indent = 0;
760   ctx.printf = dump_ctx_printf;
761   ctx.indentation = 0;
762
763   tgsi_iterate_shader( tokens, &ctx.iter );
764}
765
766struct str_dump_ctx
767{
768   struct dump_ctx base;
769   char *str;
770   char *ptr;
771   int left;
772};
773
774static void
775str_dump_ctx_printf(struct dump_ctx *ctx, const char *format, ...)
776{
777   struct str_dump_ctx *sctx = (struct str_dump_ctx *)ctx;
778
779   if(sctx->left > 1) {
780      int written;
781      va_list ap;
782      va_start(ap, format);
783      written = util_vsnprintf(sctx->ptr, sctx->left, format, ap);
784      va_end(ap);
785
786      /* Some complicated logic needed to handle the return value of
787       * vsnprintf:
788       */
789      if (written > 0) {
790         written = MIN2(sctx->left, written);
791         sctx->ptr += written;
792         sctx->left -= written;
793      }
794   }
795}
796
797void
798tgsi_dump_str(
799   const struct tgsi_token *tokens,
800   uint flags,
801   char *str,
802   size_t size)
803{
804   struct str_dump_ctx ctx;
805
806   ctx.base.iter.prolog = prolog;
807   ctx.base.iter.iterate_instruction = iter_instruction;
808   ctx.base.iter.iterate_declaration = iter_declaration;
809   ctx.base.iter.iterate_immediate = iter_immediate;
810   ctx.base.iter.iterate_property = iter_property;
811   ctx.base.iter.epilog = NULL;
812
813   ctx.base.instno = 0;
814   ctx.base.indent = 0;
815   ctx.base.printf = &str_dump_ctx_printf;
816   ctx.base.indentation = 0;
817
818   ctx.str = str;
819   ctx.str[0] = 0;
820   ctx.ptr = str;
821   ctx.left = (int)size;
822
823   tgsi_iterate_shader( tokens, &ctx.base.iter );
824}
825