KhronosGroup / glslang

Khronos-reference front end for GLSL/ESSL, partial front end for HLSL, and a SPIR-V generator.
Other
2.9k stars 816 forks source link

Make C interface SPIR-V generation idempotent #3501

Closed bjornbytes closed 4 months ago

bjornbytes commented 4 months ago

Each time you call glslang_program_SPIRV_generate, it appends the output to whatever has been previously generated. Here's an example:

#include "glslang_c_interface.h"
#include "resource_limits_c.h"
#include <stdlib.h>
#include <stdio.h>

int main() {
  const char* source = "#version 460\n"
    "void main() { gl_Position = vec4(1.); }";

  glslang_initialize_process();
  glslang_program_t* program = glslang_program_create();

  glslang_input_t input = {
    .language = GLSLANG_SOURCE_GLSL,
    .stage = GLSLANG_STAGE_VERTEX,
    .client = GLSLANG_CLIENT_VULKAN,
    .client_version = GLSLANG_TARGET_VULKAN_1_1,
    .target_language = GLSLANG_TARGET_SPV,
    .target_language_version = GLSLANG_TARGET_SPV_1_6,
    .code = source,
    .default_version = 460,
    .default_profile = GLSLANG_NO_PROFILE,
    .forward_compatible = true,
    .resource = glslang_default_resource()
  };

  glslang_shader_t* shader = glslang_shader_create(&input);

  if (!glslang_shader_preprocess(shader, &input)) {
    fprintf(stderr, "Preprocessing failed: %s\n", glslang_shader_get_info_log(shader));
    exit(EXIT_FAILURE);
  }

  if (!glslang_shader_parse(shader, &input)) {
    fprintf(stderr, "Parsing failed: %s", glslang_shader_get_info_log(shader));
    exit(EXIT_FAILURE);
  }

  glslang_program_add_shader(program, shader);

  if (!glslang_program_link(program, 0)) {
    fprintf(stderr, "Linking failed: %s", glslang_program_get_info_log(program));
  }

  glslang_program_map_io(program);

  for (int i = 0; i < 3; i++) {
    glslang_program_SPIRV_generate_with_options(program, GLSLANG_STAGE_VERTEX);

    void* words = glslang_program_SPIRV_get_ptr(program);
    size_t count = glslang_program_SPIRV_get_size(program);

    printf("Generate #%d: %zu words\n", i + 1, count);
  }

  glslang_program_delete(program);
  glslang_shader_delete(shader);
  glslang_finalize_process();
  return 0;
}

Before this change, the example prints:

Generate #1: 166 words
Generate #2: 332 words
Generate #3: 498 words

With this change, it prints:

Generate #1: 166 words
Generate #2: 166 words
Generate #3: 166 words
CLAassistant commented 4 months ago

CLA assistant check
All committers have signed the CLA.

arcady-lunarg commented 4 months ago

While I like this idea, this is technically a break in backwards compatibility, albeit in a case that I don't think should be too useful to anyone, so I'm curious if anyone else has any opinions on this.

bjornbytes commented 4 months ago

If it helps, here's the use case for my project: I am creating a single program object, adding vertex and fragment shaders to it, linking, and then querying the SPIR-V blob for each stage. The vertex stage comes back valid, but the SPIR-V generated for the fragment stage is a concatenation of the vertex and fragment stages, which is invalid.

arcady-lunarg commented 4 months ago

I'm going to let this sit for just a bit longer before I merge it in case anyone else has any comments, but I suspect that any potential comments would only come after I merge the change. Though I don't expect anyone to be relying on the current behavior, you never know.