Kaynağa Gözat

Merge pull request #834 from floooh/sgfx-pass-overhaul

Separate load/store actions for render passes.
Andre Weissflog 2 yıl önce
ebeveyn
işleme
da0608d6e5
5 değiştirilmiş dosya ile 1537 ekleme ve 243 silme
  1. 37 0
      CHANGELOG.md
  2. 4 4
      README.md
  3. 468 191
      sokol_gfx.h
  4. 963 14
      tests/functional/sokol_gfx_test.c
  5. 65 34
      util/sokol_gfx_imgui.h

+ 37 - 0
CHANGELOG.md

@@ -1,5 +1,42 @@
 ## Updates
 ## Updates
 
 
+- **19-May-2023**: _**BREAKING CHANGES**_ in sokol_gfx.h: Render passes are now more 'harmonized'
+  with Metal and WebGPU by exposing a 'store action', and making MSAA resolve attachments
+  explicit. The changes in detail:
+  - A new documentation section `ON RENDER PASSES` has been added to sokol_gfx.h, this
+    gives a much more detailed overview of the new render pass behaviour than this
+    changelog, please make sure to give it a read - especially when you are using
+    MSAA offline render passes in your code.
+  - `sg_action` has been renamed to `sg_load_action`.
+  - A new enum `sg_store_action` has been added.
+  - In `sg_pass_action`:
+    - `.action` has been renamed to `.load_action`.
+    - `.value` has been renamed to `.clear_value`.
+    - A new field `store_action` has been added.
+  - An `sg_image` object with a sample count > 1 no longer maintains a separate
+    internal msaa-resolve texture, instead resolve textures are now separate
+    `sg_image` objects.
+  - When creating a pass object, there's now a separate array of `resolve_attachments[]`,
+    when a `resolve_attachment` has been set, the `color_attachment` at the same slot
+    must be an image with a sample count > 1, and an 'msaa-resolve' operation from the
+    color attachment into the resolve attachment will take place in `sg_end_pass()`.
+  - Pass attachments are now more flexible (there were a couple of gaps where specific
+    image types were not allowed as pass attachments, especially for the depth-stencil-
+    attachment - but this hadn't actually been checked by the validation layer).
+  - Some gaps in the validation layer around images and passes have been tightened up,
+    those usually don't work in one backend or another, but have been ignored so far
+    in the validation layer, mainly:
+    - MSAA images must have num_mipmaps = 1.
+    - 3D images cannot have a sample_count > 1.
+    - 3D images cannot have depth or depth-stencil image formats.
+    - It's not allowed to bind MSAA images as texture.
+    - It's not allowed to bind depth or depth-stencil images as texture.
+    - (I'll see if I can relax some of those restrictions after the WebGPU backend release)
+  - **A lot** of new tests have been added to cover validation layer checks when creating
+    image and pass objects.
+
+  Next up: WebGPU!
+
 - **30-Apr-2023**: GLES2/WebGL1 support has been removed from the sokol headers (now that
 - **30-Apr-2023**: GLES2/WebGL1 support has been removed from the sokol headers (now that
   all browsers support WebGL2, and WebGPU is around the corner I feel like it's finally
   all browsers support WebGL2, and WebGPU is around the corner I feel like it's finally
   time to ditch GLES2.
   time to ditch GLES2.

+ 4 - 4
README.md

@@ -4,7 +4,7 @@ Simple
 [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
 [STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
 cross-platform libraries for C and C++, written in C.
 cross-platform libraries for C and C++, written in C.
 
 
-[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**30-Apr-2023** breaking change: GLES2/WebGL1 support has been removed!)
+[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**19-May-2023** BREAKING CHANGES in sokol_gfx.h around render pass behaviour)
 
 
 [![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)
 [![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)
 
 
@@ -219,13 +219,13 @@ void init(void) {
         .logger.func = slog_func,
         .logger.func = slog_func,
     });
     });
     pass_action = (sg_pass_action) {
     pass_action = (sg_pass_action) {
-        .colors[0] = { .action=SG_ACTION_CLEAR, .value={1.0f, 0.0f, 0.0f, 1.0f} }
+        .colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={1.0f, 0.0f, 0.0f, 1.0f} }
     };
     };
 }
 }
 
 
 void frame(void) {
 void frame(void) {
-    float g = pass_action.colors[0].value.g + 0.01f;
-    pass_action.colors[0].value.g = (g > 1.0f) ? 0.0f : g;
+    float g = pass_action.colors[0].clear_value.g + 0.01f;
+    pass_action.colors[0].clear_value.g = (g > 1.0f) ? 0.0f : g;
     sg_begin_default_pass(&pass_action, sapp_width(), sapp_height());
     sg_begin_default_pass(&pass_action, sapp_width(), sapp_height());
     sg_end_pass();
     sg_end_pass();
     sg_commit();
     sg_commit();

Dosya farkı çok büyük olduğundan ihmal edildi
+ 468 - 191
sokol_gfx.h


+ 963 - 14
tests/functional/sokol_gfx_test.c

@@ -11,8 +11,9 @@
 
 
 #define T(b) EXPECT_TRUE(b)
 #define T(b) EXPECT_TRUE(b)
 
 
+#define MAX_LOGITEMS (32)
 static int num_log_called = 0;
 static int num_log_called = 0;
-static sg_log_item log_item;
+static sg_log_item log_items[MAX_LOGITEMS];
 
 
 static void test_logger(const char* tag, uint32_t log_level, uint32_t log_item_id, const char* message_or_null, uint32_t line_nr, const char* filename_or_null, void* user_data) {
 static void test_logger(const char* tag, uint32_t log_level, uint32_t log_item_id, const char* message_or_null, uint32_t line_nr, const char* filename_or_null, void* user_data) {
     (void)tag;
     (void)tag;
@@ -21,15 +22,21 @@ static void test_logger(const char* tag, uint32_t log_level, uint32_t log_item_i
     (void)line_nr;
     (void)line_nr;
     (void)filename_or_null;
     (void)filename_or_null;
     (void)user_data;
     (void)user_data;
-    num_log_called++;
-    log_item = log_item_id;
+    if (num_log_called < MAX_LOGITEMS) {
+        log_items[num_log_called++] = log_item_id;
+    }
     if (message_or_null) {
     if (message_or_null) {
         printf("%s\n", message_or_null);
         printf("%s\n", message_or_null);
     }
     }
 }
 }
 
 
-static void setup(const sg_desc* desc) {
+static void reset_log_items(void) {
     num_log_called = 0;
     num_log_called = 0;
+    memset(log_items, 0, sizeof(log_items));
+}
+
+static void setup(const sg_desc* desc) {
+    reset_log_items();
     sg_desc desc_with_logger = *desc;
     sg_desc desc_with_logger = *desc;
     desc_with_logger.logger.func = test_logger;
     desc_with_logger.logger.func = test_logger;
     sg_setup(&desc_with_logger);
     sg_setup(&desc_with_logger);
@@ -1437,7 +1444,7 @@ UTEST(sokol_gfx, make_dealloc_buffer_warns) {
     sg_buffer buf = create_buffer();
     sg_buffer buf = create_buffer();
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID);
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID);
     sg_dealloc_buffer(buf);
     sg_dealloc_buffer(buf);
-    T(log_item == SG_LOGITEM_DEALLOC_BUFFER_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_DEALLOC_BUFFER_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID);
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID);
     sg_destroy_buffer(buf);
     sg_destroy_buffer(buf);
@@ -1450,7 +1457,7 @@ UTEST(sokol_gfx, make_dealloc_image_warns) {
     sg_image img = create_image();
     sg_image img = create_image();
     T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID);
     T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID);
     sg_dealloc_image(img);
     sg_dealloc_image(img);
-    T(log_item == SG_LOGITEM_DEALLOC_IMAGE_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_DEALLOC_IMAGE_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID);
     T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID);
     sg_destroy_image(img);
     sg_destroy_image(img);
@@ -1463,7 +1470,7 @@ UTEST(sokol_gfx, make_dealloc_shader_warns) {
     sg_shader shd = create_shader();
     sg_shader shd = create_shader();
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID);
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID);
     sg_dealloc_shader(shd);
     sg_dealloc_shader(shd);
-    T(log_item == SG_LOGITEM_DEALLOC_SHADER_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_DEALLOC_SHADER_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID);
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID);
     sg_destroy_shader(shd);
     sg_destroy_shader(shd);
@@ -1476,7 +1483,7 @@ UTEST(sokol_gfx, make_dealloc_pipeline_warns) {
     sg_pipeline pip = create_pipeline();
     sg_pipeline pip = create_pipeline();
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID);
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID);
     sg_dealloc_pipeline(pip);
     sg_dealloc_pipeline(pip);
-    T(log_item == SG_LOGITEM_DEALLOC_PIPELINE_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_DEALLOC_PIPELINE_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID);
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID);
     sg_destroy_pipeline(pip);
     sg_destroy_pipeline(pip);
@@ -1489,7 +1496,7 @@ UTEST(sokol_gfx, make_dealloc_pass_warns) {
     sg_pass pass = create_pass();
     sg_pass pass = create_pass();
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_VALID);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_VALID);
     sg_dealloc_pass(pass);
     sg_dealloc_pass(pass);
-    T(log_item == SG_LOGITEM_DEALLOC_PASS_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_DEALLOC_PASS_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_VALID);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_VALID);
     sg_destroy_pass(pass);
     sg_destroy_pass(pass);
@@ -1502,7 +1509,7 @@ UTEST(sokol_gfx, alloc_uninit_buffer_warns) {
     sg_buffer buf = sg_alloc_buffer();
     sg_buffer buf = sg_alloc_buffer();
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC);
     sg_uninit_buffer(buf);
     sg_uninit_buffer(buf);
-    T(log_item == SG_LOGITEM_UNINIT_BUFFER_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_UNINIT_BUFFER_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC);
     sg_shutdown();
     sg_shutdown();
@@ -1513,7 +1520,7 @@ UTEST(sokol_gfx, alloc_uninit_image_warns) {
     sg_image img = sg_alloc_image();
     sg_image img = sg_alloc_image();
     T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC);
     sg_uninit_image(img);
     sg_uninit_image(img);
-    T(log_item == SG_LOGITEM_UNINIT_IMAGE_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_UNINIT_IMAGE_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC);
     sg_shutdown();
     sg_shutdown();
@@ -1524,7 +1531,7 @@ UTEST(sokol_gfx, alloc_uninit_shader_warns) {
     sg_shader shd = sg_alloc_shader();
     sg_shader shd = sg_alloc_shader();
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC);
     sg_uninit_shader(shd);
     sg_uninit_shader(shd);
-    T(log_item == SG_LOGITEM_UNINIT_SHADER_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_UNINIT_SHADER_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC);
     sg_shutdown();
     sg_shutdown();
@@ -1535,7 +1542,7 @@ UTEST(sokol_gfx, alloc_uninit_pipeline_warns) {
     sg_pipeline pip = sg_alloc_pipeline();
     sg_pipeline pip = sg_alloc_pipeline();
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC);
     sg_uninit_pipeline(pip);
     sg_uninit_pipeline(pip);
-    T(log_item == SG_LOGITEM_UNINIT_PIPELINE_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_UNINIT_PIPELINE_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC);
     sg_shutdown();
     sg_shutdown();
@@ -1546,7 +1553,7 @@ UTEST(sokol_gfx, alloc_uninit_pass_warns) {
     sg_pass pass = sg_alloc_pass();
     sg_pass pass = sg_alloc_pass();
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_ALLOC);
     sg_uninit_pass(pass);
     sg_uninit_pass(pass);
-    T(log_item == SG_LOGITEM_UNINIT_PASS_INVALID_STATE);
+    T(log_items[0] == SG_LOGITEM_UNINIT_PASS_INVALID_STATE);
     T(num_log_called == 1);
     T(num_log_called == 1);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_ALLOC);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_ALLOC);
     sg_shutdown();
     sg_shutdown();
@@ -1640,3 +1647,945 @@ UTEST(sokol_gfx, make_pass_with_nonvalid_color_images) {
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_INVALID);
     T(sg_query_pass_state(pass) == SG_RESOURCESTATE_INVALID);
     sg_shutdown();
     sg_shutdown();
 }
 }
+
+UTEST(sokol_gfx, make_buffer_validate_start_canary) {
+    setup(&(sg_desc){0});
+    const uint32_t data[32] = {0};
+    sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){
+        ._start_canary = 1234,
+        .data = SG_RANGE(data),
+    });
+    T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_CANARY);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_buffer_validate_end_canary) {
+    setup(&(sg_desc){0});
+    const uint32_t data[32] = {0};
+    sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){
+        .data = SG_RANGE(data),
+        ._end_canary = 1234,
+    });
+    T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_CANARY);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_buffer_validate_immutable_nodata) {
+    setup(&(sg_desc){0});
+    sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ 0 });
+    T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_SIZE);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_BUFFERDESC_DATA);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_buffer_validate_size_mismatch) {
+    setup(&(sg_desc){0});
+    const uint32_t data[16] = { 0 };
+    sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){
+        .size = 15 * sizeof(uint32_t),
+        .data = SG_RANGE(data),
+    });
+    T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_DATA_SIZE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_buffer_validate_data_ptr_but_no_size) {
+    setup(&(sg_desc){0});
+    const uint32_t data[16] = {0};
+    sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){
+        .data.ptr = data,
+    });
+    T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_SIZE);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_BUFFERDESC_DATA);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_start_canary) {
+    setup(&(sg_desc){0});
+    const uint32_t pixels[8][8] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        ._start_canary = 1234,
+        .width = 8,
+        .height = 8,
+        .data.subimage[0][0] = SG_RANGE(pixels),
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_CANARY);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_end_canary) {
+    setup(&(sg_desc){0});
+    const uint32_t pixels[8][8] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .data.subimage[0][0] = SG_RANGE(pixels),
+        ._end_canary = 1234,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_CANARY);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_zero_width_height) {
+    setup(&(sg_desc){0});
+    const uint32_t pixels[8][8] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 0,
+        .height = 0,
+        .data.subimage[0][0] = SG_RANGE(pixels),
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_WIDTH);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDESC_HEIGHT);
+    T(log_items[2] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE);
+    T(log_items[3] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_msaa_no_rt) {
+    setup(&(sg_desc){0});
+    const uint32_t pixels[8][8] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .sample_count = 4,
+        .data.subimage[0][0] = SG_RANGE(pixels),
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_msaa_num_mipmaps) {
+    setup(&(sg_desc){0});
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+        .num_mipmaps = 2,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_msaa_3d_image) {
+    setup(&(sg_desc){0});
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_3D,
+        .width = 32,
+        .height = 32,
+        .num_slices = 32,
+        .sample_count = 4,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_MSAA_3D_IMAGE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_depth_3d_image_with_depth_format) {
+    setup(&(sg_desc){0});
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_3D,
+        .width = 8,
+        .height = 8,
+        .num_slices = 8,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_rt_immutable) {
+    setup(&(sg_desc){0});
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .usage = SG_USAGE_DYNAMIC,
+        .width = 8,
+        .height = 8,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_RT_IMMUTABLE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_dynamic_no_data) {
+    setup(&(sg_desc){0});
+    uint32_t pixels[8][8] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .usage = SG_USAGE_DYNAMIC,
+        .data.subimage[0][0] = SG_RANGE(pixels),
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_valiate_compressed_immutable) {
+    setup(&(sg_desc){0});
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .pixel_format = SG_PIXELFORMAT_BC1_RGBA,
+        .usage = SG_USAGE_DYNAMIC,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_nodata) {
+    setup(&(sg_desc){0});
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_NODATA);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_data_size) {
+    setup(&(sg_desc){0});
+    uint32_t pixels[4][4] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .data.subimage[0][0] = SG_RANGE(pixels),
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_missing_mipdata) {
+    setup(&(sg_desc){0});
+    uint32_t mip0[8][8] = {0};
+    uint32_t mip1[4][4] = {0};
+    uint32_t mip2[2][2] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .num_mipmaps = 4,
+        .data.subimage[0][0] = SG_RANGE(mip0),
+        .data.subimage[0][1] = SG_RANGE(mip1),
+        .data.subimage[0][2] = SG_RANGE(mip2),
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_NODATA);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_image_validate_wrong_mipsize) {
+    setup(&(sg_desc){0});
+    uint32_t mip0[8][8] = {0};
+    uint32_t mip1[4][4] = {0};
+    uint32_t mip2[2][2] = {0};
+    uint32_t mip3[1][1] = {0};
+    sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .num_mipmaps = 4,
+        .data.subimage[0][0] = SG_RANGE(mip0),
+        .data.subimage[0][1] = SG_RANGE(mip2),
+        .data.subimage[0][2] = SG_RANGE(mip1),
+        .data.subimage[0][3] = SG_RANGE(mip3)
+    });
+    T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_start_canary) {
+    setup(&(sg_desc){0});
+    sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        ._start_canary = 1234,
+        .color_attachments[0].image = sg_make_image(&(sg_image_desc){
+            .render_target = true,
+            .width = 64,
+            .height = 64,
+        }),
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_CANARY);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_end_canary) {
+    setup(&(sg_desc){0});
+    sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = sg_make_image(&(sg_image_desc){
+            .render_target = true,
+            .width = 64,
+            .height = 64,
+        }),
+        ._end_canary = 1234,
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_CANARY);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_no_color_attrs) {
+    setup(&(sg_desc){0});
+    // FIXME: rendering without color attachments but depth attachment should actually work
+    sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .depth_stencil_attachment.image = sg_make_image(&(sg_image_desc){
+            .render_target = true,
+            .width = 64,
+            .height = 64,
+            .pixel_format = SG_PIXELFORMAT_DEPTH,
+        })
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_NO_COLOR_ATTS);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES);
+    T(log_items[2] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES);
+    T(log_items[3] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SAMPLE_COUNT);
+    T(log_items[4] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_no_cont_color_atts1) {
+    setup(&(sg_desc){0});
+    const sg_image_desc img_desc = { .render_target = true, .width = 64, .height = 64 };
+    sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments = {
+            [0].image = sg_make_image(&img_desc),
+            [2].image = sg_make_image(&img_desc),
+        }
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_image) {
+    setup(&(sg_desc){0});
+    const sg_image_desc img_desc = { .render_target = true, .width = 64, .height = 64 };
+    const sg_image img0 = sg_make_image(&img_desc);
+    const sg_image img1 = sg_make_image(&img_desc);
+    sg_destroy_image(img1);
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments = {
+            [0].image = img0,
+            [1].image = img1,
+        }
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_IMAGE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_miplevel) {
+    setup(&(sg_desc){0});
+    const sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 16,
+        .height = 16,
+        .num_mipmaps = 4,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = img, .mip_level = 4 }
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_MIPLEVEL);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_face) {
+    setup(&(sg_desc){0});
+    const sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_CUBE,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = img, .slice = 6 }
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_FACE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_layer) {
+    setup(&(sg_desc){0});
+    const sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_ARRAY,
+        .width = 64,
+        .height = 64,
+        .num_slices = 4,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = img, .slice = 5 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_LAYER);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_slice) {
+    setup(&(sg_desc){0});
+    const sg_image img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_3D,
+        .width = 64,
+        .height = 64,
+        .num_slices = 4,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = img, .slice = 5 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_SLICE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_image_no_rt) {
+    setup(&(sg_desc){0});
+    const sg_image img = sg_make_image(&(sg_image_desc){
+        .width = 8,
+        .height = 8,
+        .usage = SG_USAGE_DYNAMIC,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = img,
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_IMAGE_NO_RT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_color_inv_pixelformat) {
+    setup(&(sg_desc){0});
+    const sg_image_desc img_desc = {
+        .render_target = true,
+        .width = 8,
+        .height = 8,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    };
+    reset_log_items();
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = sg_make_image(&img_desc),
+        .depth_stencil_attachment.image = sg_make_image(&img_desc),
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_depth_inv_pixelformat) {
+    setup(&(sg_desc){0});
+    const sg_image_desc img_desc = {
+        .render_target = true,
+        .width = 8,
+        .height = 8,
+    };
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = sg_make_image(&img_desc),
+        .depth_stencil_attachment.image = sg_make_image(&img_desc),
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_image_sizes) {
+    setup(&(sg_desc){0});
+    const sg_image img0 = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_image img1 = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 32,
+        .height = 32,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments = {
+            [0].image = img0,
+            [1].image = img1,
+        }
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_IMAGE_SIZES);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_PASSDESC_IMAGE_SIZES);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_image_sample_counts) {
+    setup(&(sg_desc){0});
+    const sg_image img0 = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image img1 = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 2,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments = {
+            [0].image = img0,
+            [1].image = img1,
+        }
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_color_image_msaa) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 1,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = color_img,
+        .resolve_attachments[0].image = resolve_img,
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_COLOR_IMAGE_MSAA);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_image) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 1,
+    });
+    sg_destroy_image(resolve_img);
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = color_img,
+        .resolve_attachments[0].image = resolve_img,
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_sample_count) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0].image = color_img,
+        .resolve_attachments[0].image = resolve_img,
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_SAMPLE_COUNT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_miplevel) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img, .mip_level = 1 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_MIPLEVEL);
+    // FIXME: these are confusing
+    T(log_items[1] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES);
+    T(log_items[2] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES);
+    T(log_items[3] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_face) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_CUBE,
+        .width = 64,
+        .height = 64,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img, .slice = 6 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_FACE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_layer) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_ARRAY,
+        .width = 64,
+        .height = 64,
+        .num_slices = 4,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img, .slice = 4 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_LAYER);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_slice) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_3D,
+        .width = 64,
+        .height = 64,
+        .num_slices = 4,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img, .slice = 4 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_SLICE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_image_no_rt) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .width = 64,
+        .height = 64,
+        .usage = SG_USAGE_DYNAMIC,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE_NO_RT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_image_sizes) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 32,
+        .height = 32,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_resolve_image_format) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image resolve_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .pixel_format = SG_PIXELFORMAT_R8,
+        .sample_count = 1,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .resolve_attachments[0] = { .image = resolve_img },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_RESOLVE_IMAGE_FORMAT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_depth_image) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_image depth_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    });
+    sg_destroy_image(depth_img);
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .depth_stencil_attachment.image = depth_img,
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_depth_miplevel) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_image depth_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .depth_stencil_attachment = { .image = depth_img, .mip_level = 1 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_MIPLEVEL);
+    // FIXME: these additional validation errors are confusing
+    T(log_items[1] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES);
+    T(log_items[2] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES);
+    T(log_items[3] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_depth_face) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_image depth_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_CUBE,
+        .width = 64,
+        .height = 64,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .depth_stencil_attachment = { .image = depth_img, .slice = 6 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_FACE);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_depth_layer) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_image depth_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .type = SG_IMAGETYPE_ARRAY,
+        .width = 64,
+        .height = 64,
+        .num_slices = 4,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .depth_stencil_attachment = { .image = depth_img, .slice = 4 },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_LAYER);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+// NOTE: VALIDATE_PASSDESC_DEPTH_SLICE can't actually happen because VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE
+
+// NOTE: VALIDATE_DEPTH_IMAGE_NO_RT can't actually happen because VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT
+
+UTEST(sokol_gfx, make_pass_validate_depth_image_sizes) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+    });
+    const sg_image depth_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 32,
+        .height = 32,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .depth_stencil_attachment = { .image = depth_img },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES);
+    T(log_items[1] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES);
+    T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}
+
+UTEST(sokol_gfx, make_pass_validate_depth_image_sample_count) {
+    setup(&(sg_desc){0});
+    const sg_image color_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .sample_count = 4,
+    });
+    const sg_image depth_img = sg_make_image(&(sg_image_desc){
+        .render_target = true,
+        .width = 64,
+        .height = 64,
+        .pixel_format = SG_PIXELFORMAT_DEPTH,
+        .sample_count = 2,
+    });
+    const sg_pass pass = sg_make_pass(&(sg_pass_desc){
+        .color_attachments[0] = { .image = color_img },
+        .depth_stencil_attachment = { .image = depth_img },
+    });
+    T(sg_query_pass_state(pass) == SG_RESOURCESTATE_FAILED);
+    T(log_items[0] == SG_LOGITEM_VALIDATE_PASSDESC_DEPTH_IMAGE_SAMPLE_COUNT);
+    T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED);
+    sg_shutdown();
+}

+ 65 - 34
util/sokol_gfx_imgui.h

@@ -274,6 +274,7 @@ typedef struct sg_imgui_pass_t {
     sg_pass res_id;
     sg_pass res_id;
     sg_imgui_str_t label;
     sg_imgui_str_t label;
     float color_image_scale[SG_MAX_COLOR_ATTACHMENTS];
     float color_image_scale[SG_MAX_COLOR_ATTACHMENTS];
+    float resolve_image_scale[SG_MAX_COLOR_ATTACHMENTS];
     float ds_image_scale;
     float ds_image_scale;
     sg_pass_desc desc;
     sg_pass_desc desc;
 } sg_imgui_pass_t;
 } sg_imgui_pass_t;
@@ -1579,6 +1580,7 @@ _SOKOL_PRIVATE void _sg_imgui_pass_created(sg_imgui_t* ctx, sg_pass res_id, int
     pass->res_id = res_id;
     pass->res_id = res_id;
     for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
     for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
         pass->color_image_scale[i] = 0.25f;
         pass->color_image_scale[i] = 0.25f;
+        pass->resolve_image_scale[i] = 0.25f;
     }
     }
     pass->ds_image_scale = 0.25f;
     pass->ds_image_scale = 0.25f;
     pass->label = _sg_imgui_make_str(desc->label);
     pass->label = _sg_imgui_make_str(desc->label);
@@ -3205,14 +3207,17 @@ _SOKOL_PRIVATE void _sg_imgui_draw_buffer_panel(sg_imgui_t* ctx, sg_buffer buf)
     }
     }
 }
 }
 
 
-_SOKOL_PRIVATE bool _sg_imgui_image_renderable(sg_image_type type, sg_pixel_format fmt) {
-    return (type == SG_IMAGETYPE_2D) && sg_query_pixelformat(fmt).sample && !sg_query_pixelformat(fmt).depth;
+_SOKOL_PRIVATE bool _sg_imgui_image_renderable(sg_image_type type, sg_pixel_format fmt, int sample_count) {
+    return (type == SG_IMAGETYPE_2D)
+        && sg_query_pixelformat(fmt).sample
+        && !sg_query_pixelformat(fmt).depth
+        && sample_count == 1;
 }
 }
 
 
 _SOKOL_PRIVATE void _sg_imgui_draw_embedded_image(sg_imgui_t* ctx, sg_image img, float* scale) {
 _SOKOL_PRIVATE void _sg_imgui_draw_embedded_image(sg_imgui_t* ctx, sg_image img, float* scale) {
     if (sg_query_image_state(img) == SG_RESOURCESTATE_VALID) {
     if (sg_query_image_state(img) == SG_RESOURCESTATE_VALID) {
         sg_imgui_image_t* img_ui = &ctx->images.slots[_sg_imgui_slot_index(img.id)];
         sg_imgui_image_t* img_ui = &ctx->images.slots[_sg_imgui_slot_index(img.id)];
-        if (_sg_imgui_image_renderable(img_ui->desc.type, img_ui->desc.pixel_format)) {
+        if (_sg_imgui_image_renderable(img_ui->desc.type, img_ui->desc.pixel_format, img_ui->desc.sample_count)) {
             igPushID_Int((int)img.id);
             igPushID_Int((int)img.id);
             igSliderFloat("Scale", scale, 0.125f, 8.0f, "%.3f", ImGuiSliderFlags_Logarithmic);
             igSliderFloat("Scale", scale, 0.125f, 8.0f, "%.3f", ImGuiSliderFlags_Logarithmic);
             float w = (float)img_ui->desc.width * (*scale);
             float w = (float)img_ui->desc.width * (*scale);
@@ -3238,24 +3243,24 @@ _SOKOL_PRIVATE void _sg_imgui_draw_image_panel(sg_imgui_t* ctx, sg_image img) {
             igSeparator();
             igSeparator();
             _sg_imgui_draw_embedded_image(ctx, img, &img_ui->ui_scale);
             _sg_imgui_draw_embedded_image(ctx, img, &img_ui->ui_scale);
             igSeparator();
             igSeparator();
-            igText("Type:              %s", _sg_imgui_imagetype_string(desc->type));
-            igText("Usage:             %s", _sg_imgui_usage_string(desc->usage));
-            igText("Render Target:     %s", _sg_imgui_bool_string(desc->render_target));
-            igText("Width:             %d", desc->width);
-            igText("Height:            %d", desc->height);
-            igText("Num Slices:        %d", desc->num_slices);
-            igText("Num Mipmaps:       %d", desc->num_mipmaps);
-            igText("Pixel Format:      %s", _sg_imgui_pixelformat_string(desc->pixel_format));
-            igText("Sample Count:      %d", desc->sample_count);
-            igText("Min Filter:        %s", _sg_imgui_filter_string(desc->min_filter));
-            igText("Mag Filter:        %s", _sg_imgui_filter_string(desc->mag_filter));
-            igText("Wrap U:            %s", _sg_imgui_wrap_string(desc->wrap_u));
-            igText("Wrap V:            %s", _sg_imgui_wrap_string(desc->wrap_v));
-            igText("Wrap W:            %s", _sg_imgui_wrap_string(desc->wrap_w));
-            igText("Border Color:      %s", _sg_imgui_bordercolor_string(desc->border_color));
-            igText("Max Anisotropy:    %d", desc->max_anisotropy);
-            igText("Min LOD:           %.3f", desc->min_lod);
-            igText("Max LOD:           %.3f", desc->max_lod);
+            igText("Type:           %s", _sg_imgui_imagetype_string(desc->type));
+            igText("Usage:          %s", _sg_imgui_usage_string(desc->usage));
+            igText("Render Target:  %s", _sg_imgui_bool_string(desc->render_target));
+            igText("Width:          %d", desc->width);
+            igText("Height:         %d", desc->height);
+            igText("Num Slices:     %d", desc->num_slices);
+            igText("Num Mipmaps:    %d", desc->num_mipmaps);
+            igText("Pixel Format:   %s", _sg_imgui_pixelformat_string(desc->pixel_format));
+            igText("Sample Count:   %d", desc->sample_count);
+            igText("Min Filter:     %s", _sg_imgui_filter_string(desc->min_filter));
+            igText("Mag Filter:     %s", _sg_imgui_filter_string(desc->mag_filter));
+            igText("Wrap U:         %s", _sg_imgui_wrap_string(desc->wrap_u));
+            igText("Wrap V:         %s", _sg_imgui_wrap_string(desc->wrap_v));
+            igText("Wrap W:         %s", _sg_imgui_wrap_string(desc->wrap_w));
+            igText("Border Color:   %s", _sg_imgui_bordercolor_string(desc->border_color));
+            igText("Max Anisotropy: %d", desc->max_anisotropy);
+            igText("Min LOD:        %.3f", desc->min_lod);
+            igText("Max LOD:        %.3f", desc->max_lod);
             if (desc->usage != SG_USAGE_IMMUTABLE) {
             if (desc->usage != SG_USAGE_IMMUTABLE) {
                 igSeparator();
                 igSeparator();
                 igText("Num Slots:     %d", info.num_slots);
                 igText("Num Slots:     %d", info.num_slots);
@@ -3538,6 +3543,14 @@ _SOKOL_PRIVATE void _sg_imgui_draw_pass_panel(sg_imgui_t* ctx, sg_pass pass) {
                 igText("Color Attachment #%d:", i);
                 igText("Color Attachment #%d:", i);
                 _sg_imgui_draw_pass_attachment(ctx, &pass_ui->desc.color_attachments[i], &pass_ui->color_image_scale[i]);
                 _sg_imgui_draw_pass_attachment(ctx, &pass_ui->desc.color_attachments[i], &pass_ui->color_image_scale[i]);
             }
             }
+            for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
+                if (pass_ui->desc.resolve_attachments[i].image.id == SG_INVALID_ID) {
+                    break;
+                }
+                igSeparator();
+                igText("Resolve Attachment #%d:", i);
+                _sg_imgui_draw_pass_attachment(ctx, &pass_ui->desc.resolve_attachments[i], &pass_ui->resolve_image_scale[i]);
+            }
             if (pass_ui->desc.depth_stencil_attachment.image.id != SG_INVALID_ID) {
             if (pass_ui->desc.depth_stencil_attachment.image.id != SG_INVALID_ID) {
                 igSeparator();
                 igSeparator();
                 igText("Depth-Stencil Attachemnt:");
                 igText("Depth-Stencil Attachemnt:");
@@ -3730,27 +3743,45 @@ _SOKOL_PRIVATE void _sg_imgui_draw_passaction_panel(sg_imgui_t* ctx, sg_pass pas
         const sg_color_attachment_action* c_att = &action->colors[i];
         const sg_color_attachment_action* c_att = &action->colors[i];
         igText("  Color Attachment %d:", i);
         igText("  Color Attachment %d:", i);
         sg_imgui_str_t color_str;
         sg_imgui_str_t color_str;
-        switch (c_att->action) {
-            case SG_ACTION_LOAD: igText("    SG_ACTION_LOAD"); break;
-            case SG_ACTION_DONTCARE: igText("    SG_ACTION_DONTCARE"); break;
-            default:
-                igText("    SG_ACTION_CLEAR: %s", _sg_imgui_color_string(&color_str, c_att->value));
+        switch (c_att->load_action) {
+            case SG_LOADACTION_LOAD: igText("    SG_LOADACTION_LOAD"); break;
+            case SG_LOADACTION_DONTCARE: igText("    SG_LOADACTION_DONTCARE"); break;
+            case SG_LOADACTION_CLEAR:
+                igText("    SG_LOADACTION_CLEAR: %s", _sg_imgui_color_string(&color_str, c_att->clear_value));
                 break;
                 break;
+            default: igText("    ???"); break;
+        }
+        switch (c_att->store_action) {
+            case SG_STOREACTION_STORE: igText("    SG_STOREACTION_STORE"); break;
+            case SG_STOREACTION_DONTCARE: igText("    SG_STOREACTION_DONTCARE"); break;
+            default: igText("    ???"); break;
         }
         }
     }
     }
     const sg_depth_attachment_action* d_att = &action->depth;
     const sg_depth_attachment_action* d_att = &action->depth;
     igText("  Depth Attachment:");
     igText("  Depth Attachment:");
-    switch (d_att->action) {
-        case SG_ACTION_LOAD: igText("    SG_ACTION_LOAD"); break;
-        case SG_ACTION_DONTCARE: igText("    SG_ACTION_DONTCARE"); break;
-        default: igText("    SG_ACTION_CLEAR: %.3f", d_att->value); break;
+    switch (d_att->load_action) {
+        case SG_LOADACTION_LOAD: igText("    SG_LOADACTION_LOAD"); break;
+        case SG_LOADACTION_DONTCARE: igText("    SG_LOADACTION_DONTCARE"); break;
+        case SG_LOADACTION_CLEAR: igText("    SG_LOADACTION_CLEAR: %.3f", d_att->clear_value); break;
+        default: igText("    ???"); break;
+    }
+    switch (d_att->store_action) {
+        case SG_STOREACTION_STORE: igText("    SG_STOREACTION_STORE"); break;
+        case SG_STOREACTION_DONTCARE: igText("    SG_STOREACTION_DONTCARE"); break;
+        default: igText("    ???"); break;
     }
     }
     const sg_stencil_attachment_action* s_att = &action->stencil;
     const sg_stencil_attachment_action* s_att = &action->stencil;
     igText("  Stencil Attachment");
     igText("  Stencil Attachment");
-    switch (s_att->action) {
-        case SG_ACTION_LOAD: igText("    SG_ACTION_LOAD"); break;
-        case SG_ACTION_DONTCARE: igText("    SG_ACTION_DONTCARE"); break;
-        default: igText("    SG_ACTION_CLEAR: 0x%02X", s_att->value); break;
+    switch (s_att->load_action) {
+        case SG_LOADACTION_LOAD: igText("    SG_LOADACTION_LOAD"); break;
+        case SG_LOADACTION_DONTCARE: igText("    SG_LOADACTION_DONTCARE"); break;
+        case SG_LOADACTION_CLEAR: igText("    SG_LOADACTION_CLEAR: 0x%02X", s_att->clear_value); break;
+        default: igText("    ???"); break;
+    }
+    switch (d_att->store_action) {
+        case SG_STOREACTION_STORE: igText("    SG_STOREACTION_STORE"); break;
+        case SG_STOREACTION_DONTCARE: igText("    SG_STOREACTION_DONTCARE"); break;
+        default: igText("    ???"); break;
     }
     }
 }
 }
 
 

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor