Унифицированное имя GLSL пусто при использовании SPIR-V на Intel HD Graphics, но не на NVIDIA

Контекст

Я пишу приложение, похожее на Shadertoy, в рамках школьного проекта и заметил, что значения моих юниформ-переменных не обновлялись на моем (старом) Thinkpad, хотя на моем настольном ПК они работали нормально. В моем приложении используются скомпилированные шейдеры GLSL (с использованием glslc в двоичном формате SPIR-V), которые компонуются с конечным исполняемым файлом и считываются с помощью функции glShaderBinary.

Я создал приложение MWE, чтобы продемонстрировать этот эффект. Приложение считывает фрагментный и вершинный шейдер как в исходном формате GLSL, так и в двоичном формате SPIR-V. Для обоих форматов он связывает фрагментный и вершинный шейдеры в одну программу (в исходном коде она называется shader) и перечисляет имена всех активных форм. Он также пытается напрямую получить местоположение униформы с именем test в shader.frag.

На Intel (i5-4200U):

vendor:        Intel
renderer:      Mesa Intel(R) HD Graphics 4400 (HSW GT2)
version:       4.6 (Core Profile) Mesa 24.0.7-arch1.3
glsl version:  4.60
------------------------------
GLSL:
    active uniform count: 1
    (location = 0) = "test"
    `test` uniform location = 0
SPIR-V:
    active uniform count: 1
    (location = 0) = ""
    `test` uniform location = -1

На NVIDIA:

vendor:        NVIDIA Corporation
renderer:      NVIDIA GeForce GTX 1050 Ti/PCIe/SSE2
version:       4.6.0 NVIDIA 550.78
glsl version:  4.60 NVIDIA
------------------------------
GLSL:
    active uniform count: 1
    (location = 0) = "test"
    `test` uniform location = 0
SPIR-V:
    active uniform count: 1
    (location = 0) = "test"
    `test` uniform location = 0

Источники

Исходники также доступны в виде репозитория git:

git clone --branch bug2 https://git.pipeframe.xyz/school/project-iprj

main.c

#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

const uint32_t vert_spirv[] =
#include "vert_spirv.h"
;
const uint32_t frag_spirv[] =
#include "frag_spirv.h"
;
const char vert_src[] = {
#include "vert_src.h"
, 0x00 };
const char frag_src[] = {
#include "frag_src.h"
, 0x00 };

GLuint load_shader_src(GLenum type, const char* src) {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &src, NULL);
    glCompileShader(shader);
    return shader;
}
GLuint load_shader_spirv(GLenum type, const char* src, size_t size) {
    GLuint shader = glCreateShader(type);
    glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V, src, size);
    glSpecializeShader(shader, "main", 0, NULL, NULL);
    return shader;
}

GLuint link_shaders(GLuint vert, GLuint frag) {
    GLuint shader = glCreateProgram();
    glAttachShader(shader, vert);
    glAttachShader(shader, frag);
    glLinkProgram(shader);
    glUseProgram(shader); // <- use the program before getting uniform name
    glDeleteShader(vert);
    glDeleteShader(frag);
    return shader;
}

void test(const char* label, GLuint shader) {
    printf("%s:\n", label);

    // list all ACTIVE uniforms in shader
    GLint count;
    glGetProgramiv(shader, GL_ACTIVE_UNIFORMS, &count);
    printf("\tactive uniform count: %d\n", count);
    for (unsigned i = 0; i < count; i++) {
        GLchar name[80];
        GLsizei length;
        glGetActiveUniformName(shader, i, 80, &length, name);
        printf("\t(location = %u) = \"%.*s\"\n", i, length, name);
    }

    // try directly geting uniform location
    GLint uniform_location = glGetUniformLocation(shader, "test");
    printf("\t`test` uniform location = %d\n", uniform_location);
}

int main(int argc, char** argv) {
    // initialize window
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    GLFWwindow* window = glfwCreateWindow(800, 600, "test", NULL, NULL);
    glfwMakeContextCurrent(window);
    glewInit();

    // print driver info
    printf("vendor:        %s\n", glGetString(GL_VENDOR));
    printf("renderer:      %s\n", glGetString(GL_RENDERER));
    printf("version:       %s\n", glGetString(GL_VERSION));
    printf("glsl version:  %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
    printf("------------------------------\n");

    // test w/ glsl source
    GLuint src_vert = load_shader_src(GL_VERTEX_SHADER, vert_src);
    GLuint src_frag = load_shader_src(GL_FRAGMENT_SHADER, frag_src);
    GLuint src_shader = link_shaders(src_vert, src_frag);
    test("GLSL", src_shader);

    // test w/ spir-v
    GLuint spirv_vert = load_shader_spirv(GL_VERTEX_SHADER, (char*) vert_spirv, sizeof(vert_spirv));
    GLuint spirv_frag = load_shader_spirv(GL_FRAGMENT_SHADER, (char*) frag_spirv, sizeof(frag_spirv));
    GLuint spirv_shader = link_shaders(spirv_vert, spirv_frag);
    test("SPIR-V", spirv_shader);

    return 0;
}

makefile (производитель GNU)

LDFLAGS += -lglfw
LDFLAGS += -lOpenGL
LDFLAGS += -lGLEW

GLFLAGS += --target-env=opengl
GLFLAGS += -fauto-map-locations

main: main.c

main.c: vert_spirv.h
main.c: frag_spirv.h
main.c: vert_src.h
main.c: frag_src.h

frag_spirv.h: shader.frag
    glslc $(GLFLAGS) -mfmt=c -o $@ $<
vert_spirv.h: shader.vert
    glslc $(GLFLAGS) -mfmt=c -o $@ $<
frag_src.h: shader.frag
    xxd -i < $< > $@
vert_src.h: shader.vert
    xxd -i < $< > $@

shader.vert

#version 460 core
layout (location = 0) in vec3 vert;

void main() {
    gl_Position = vec4(vert.xyz, 0.001);
}

shader.frag

#version 460 core
uniform float test;
layout(location = 0) out vec4 color;

void main() {
    vec2 uv = gl_FragCoord.xy / ivec2(800, 600);
    color = vec4(uv.xy, test, 1.0);
}

Проблема

Единое имя становится пустым при запуске шейдеров SPIR-V на Intel HD Graphics. Униформа test все еще активна, как показано в приведенном выше выводе. Когда я жестко запрограммировал местоположение униформы test вместо использования glGetUniformLocation, обновление его значения отлично работает как на Intel, так и на NVIDIA, но я бы хотел избежать этого подхода, поскольку у меня нет контроля над местоположением.

Я пробовал следующее, но все они не оказали никакого влияния на вывод MWE:

  • Добавление layout(location = ...) вручную перед унифицированным объявлением.
  • Использование флага glslc-fauto-bind-uniforms
  • Использование версии ARB glShaderBinary
  • Использование драйвера mesa-amber
  • Использование OpenGL 4.60

Я хотел бы продолжать использовать компилятор SPIR-V, поскольку он позволяет мне использовать препроцессор C и перехватывать предупреждения компилятора GLSL во время компиляции, а не во время выполнения.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поведение этой программы соответствует спецификациям (выделено мной):

Поскольку SPIR-V является промежуточным языком, такие вещи, как имена, не нужны. Таким образом, хотя SPIR-V и позволяет вам присвоить имя конкретной конструкции, он не требует от вас этого. Таким образом, любой запрос на самоанализ OpenGL, включающий имя переменной SPIR-V или другой конструкции, может не дать разумных результатов.

Как показывают выходные данные программы, реализация Intel обнаруживает (активную) юниформ-переменную. Только его имя невозможно достоверно запросить. Пользовательские переменные в SPIR-V могут быть связаны только с использованием местоположений:

Сопоставление интерфейсов ввода/вывода для пользовательских переменных в SPIR-V осуществляется путем сопоставления явных Location. Таким образом, всем переменным, которые используются для интерфейсов ввода/вывода, должно быть назначено местоположение.

Другие вопросы по теме