WebGPU 셰이딩 언어

W3C 후보 권고안 초안,

이 문서에 대한 추가 정보
이 버전:
https://www.w3.org/TR/2025/CRD-WGSL-20250820/
최신 공개 버전:
https://www.w3.org/TR/WGSL/
편집자 초안:
https://gpuweb.github.io/gpuweb/wgsl/
이전 버전:
히스토리:
https://www.w3.org/standards/history/WGSL/
피드백:
public-gpu@w3.org 제목 줄에 “[WGSL] … 메시지 주제 …” 포함 (아카이브)
GitHub
편집자:
(Google)
(Google)
이전 편집자:
(Apple Inc.)
(Google)
참여:
이슈 등록 (오픈 이슈)
테스트 스위트:
WebGPU CTS shader/

요약

WebGPU를 위한 셰이딩 언어입니다.

이 문서의 현황

이 섹션은 문서가 발행된 시점의 상태를 설명합니다. 현재 W3C 발행물 목록과 이 기술 보고서의 최신 개정본은 W3C 표준 및 초안 색인에서 확인할 수 있습니다.

이 명세서에 대한 피드백과 의견을 환영합니다. 이 명세서에 대한 논의는 GitHub Issues를 이용하는 것이 권장됩니다. 또는 GPU for the Web 워킹 그룹 메일링 리스트 public-gpu@w3.org (아카이브)로 의견을 보낼 수 있습니다. 이 초안에는 워킹 그룹에서 논의 중인 미해결 이슈들이 일부 하이라이트되어 있습니다. 이러한 이슈들의 결과 및 유효성에 대해서는 아직 결정되지 않았습니다.

이 문서는 GPU for the Web 워킹 그룹권고안 트랙을 이용하여 후보 권고안 초안으로 발행했습니다. 이 문서는 최소 까지 후보 권고안 상태를 유지합니다.

그룹은 각 기능의 구현을 최신 GPU 시스템 API를 기반으로 최소 두 개의 배포된 브라우저에서 시연할 것으로 기대합니다. 테스트 스위트는 구현 보고서를 작성하는 데 사용됩니다.

후보 권고안으로 발행된다고 해서 W3C 및 회원의 공식 지지를 의미하지는 않습니다. 후보 권고안 초안은 워킹 그룹이 이후의 후보 권고안 스냅샷에 포함하기로 한 이전 후보 권고안의 변경 사항을 통합합니다.

이 문서는 언제든지 유지 및 업데이트됩니다. 일부 내용은 작업 중인 부분일 수 있습니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹 산출물과 관련하여 공개된 특허 공개 목록을 유지하며, 해당 페이지에는 특허 공개 방법도 안내되어 있습니다. 본인이 필수 청구항이 포함된 특허를 실제로 알고 있다고 판단되는 경우, W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.

이 문서는 2025년 8월 18일 W3C 프로세스 문서에 따라 관리됩니다.

1. 소개

WebGPU 셰이딩 언어(WGSL)는 [WebGPU]의 셰이더 언어입니다. 즉, WebGPU API를 사용하는 애플리케이션은 GPU에서 실행되는 프로그램(셰이더라고 불림)을 표현하기 위해 WGSL을 사용합니다.

// 텍스처가 적용된 기하 도형에 점광원으로 조명을 비추는 프래그먼트 셰이더입니다.

// 스토리지 버퍼 바인딩에서 조명 정보를 가져옵니다.
struct PointLight {
  position : vec3f,
  color : vec3f,
}

struct LightStorage {
  pointCount : u32,
  point : array<PointLight>,
}
@group(0) @binding(0) var<storage> lights : LightStorage;

// 텍스처와 샘플러.
@group(1) @binding(0) var baseColorSampler : sampler;
@group(1) @binding(1) var baseColorTexture : texture_2d<f32>;

// 함수 인자는 버텍스 셰이더에서 전달된 값입니다.
@fragment
fn fragmentMain(@location(0) worldPos : vec3f,
                @location(1) normal : vec3f,
                @location(2) uv : vec2f) -> @location(0) vec4f {
  // 표면의 기본 색상을 텍스처에서 샘플링합니다.
  let baseColor = textureSample(baseColorTexture, baseColorSampler, uv);

  let N = normalize(normal);
  var surfaceColor = vec3f(0);

  // 씬의 점광원을 반복합니다.
  for (var i = 0u; i < lights.pointCount; i++) {
    let worldToLight = lights.point[i].position - worldPos;
    let dist = length(worldToLight);
    let dir = normalize(worldToLight);

    // 이 광원이 표면 색상에 미치는 기여도를 계산합니다.
    let radiance = lights.point[i].color * (1 / pow(dist, 2));
    let nDotL = max(dot(N, dir), 0);

    // 표면 색상에 빛의 기여도를 누적합니다.
    surfaceColor += baseColor.rgb * radiance * nDotL;
  }

  // 누적된 표면 색상을 반환합니다.
  return vec4(surfaceColor, baseColor.a);
}

1.1. 개요

WebGPU는 GPU 명령 형태로 GPU에 작업 단위를 전달합니다. WGSL은 두 가지 종류의 GPU 명령과 관련이 있습니다:

두 종류의 파이프라인 모두 WGSL로 작성된 셰이더를 사용합니다.

셰이더는 파이프라인에서 셰이더 단계를 실행하는 WGSL 프로그램의 일부입니다. 셰이더는 다음으로 구성됩니다:

참고: WGSL 프로그램은 엔트리 포인트가 필요하지 않지만, 엔트리 포인트가 없으면 API에서 실행할 수 없습니다. 엔트리 포인트는 GPUProgrammableStage 를 생성하는 데 필요합니다.

셰이더 단계를 실행할 때, 구현체는 다음을 수행합니다:

WGSL 프로그램은 다음으로 구성됩니다:

참고: WGSL 프로그램은 현재 하나의 WGSL 모듈로 구성되어 있습니다.

WGSL은 명령형 언어입니다: 동작은 실행할 문장들의 순서로 지정됩니다. 문장은 다음을 수행할 수 있습니다:

WGSL은 정적 타입 언어입니다: 특정 식에서 계산된 값은 프로그램 소스만을 통해 결정된 특정 타입에 속합니다.

WGSL은 불리언과 숫자 (정수, 부동소수점)를 설명하는 타입을 가집니다. 이 타입들은 합성 타입으로 집계할 수 있습니다 (벡터, 행렬, 배열, 구조체). WGSL은 고유한 연산을 제공하는 원자 타입 등 특별한 타입도 포함합니다. WGSL은 메모리에 저장할 수 있는 타입을 메모리 뷰로 설명합니다. WGSL은 텍스처와 샘플러 형태로 자주 사용하는 렌더링 타입을 제공합니다. 이러한 타입들은 내장 함수와 연계되어 GPU 하드웨어의 그래픽 렌더링 기능을 노출합니다.

WGSL은 구체적 타입 간 암시적 변환 또는 승격을 제공하지 않지만, 추상적 타입에서는 암시적 변환 및 승격을 제공합니다. 한 구체적 숫자 또는 불리언 타입에서 다른 타입으로 값을 변환하려면, 명시적인 변환, 값 생성자, 또는 비트 재해석이 필요합니다. 단, WGSL은 스칼라 타입을 벡터 타입으로 승격하는 제한적인 기능을 제공합니다. 이는 합성 타입에도 적용됩니다.

셰이더 단계의 작업은 하나 이상의 인보케이션으로 분할되며, 각각은 엔트리 포인트를 약간 다른 조건에서 실행합니다. 셰이더 단계의 인보케이션들은 특정 변수에 대한 접근을 공유합니다:

하지만 인보케이션은 서로 다른 셰이더 단계 입력 집합, 즉 인보케이션을 구분하는 식별 값을 제공하는 내장 입력을 포함한 입력 집합을 다룹니다. 각 인보케이션은 privatefunction 주소 공간에 있는 변수 형태로 독립적인 메모리 공간을 가집니다.

셰이더 단계 내 인보케이션들은 동시에, 그리고 종종 병렬로 실행됩니다. 셰이더 작성자는 셰이더 단계의 인보케이션 동적 동작에 대한 책임을 집니다:

WGSL은 특정 기능에 대해 여러 동작을 허용할 수 있습니다. 이는 이식성 위험이 될 수 있으며, 구현에 따라 다른 동작이 나타날 수 있습니다. WGSL의 설계는 이러한 경우를 최소화하는 데 목적을 두지만, 현실적 제약 및 다양한 장치에서 높은 성능을 달성하기 위한 목표에 의해 제한됩니다.

행동적 요구사항은 WGSL 프로그램을 처리하거나 실행할 때 구현체가 수행해야 할 작업을 의미합니다. 이는 프로그래머와의 계약에서 구현체가 준수해야 할 의무를 설명합니다. 명세서는 이러한 의무가 명확하지 않을 수 있는 경우에 대해 이를 명시적으로 기술합니다.

1.2. 구문 표기법

아래의 구문 표기법은 WGSL의 구문 문법 규칙을 설명합니다:

1.3. 수학 용어 및 표기법

각도:

쌍곡각은 전통적인 의미의 각도가 아니라 단위 없는 면적입니다. 구체적으로:

이때 면적 a는 쌍곡각이며, xa의 쌍곡코사인, ya의 쌍곡사인입니다.

양의 무한대(+∞로 표기)은 모든 실수보다 엄격하게 큰 고유한 값입니다.

음의 무한대(−∞로 표기)은 모든 실수보다 엄격하게 작은 고유한 값입니다.

확장 실수(affinely extended real numbers)은 실수 집합에 +∞, −∞를 포함한 집합입니다. 컴퓨터는 부동소수점 타입을 사용하여 확장 실수를 근사적으로 표현하며, 두 무한대 값도 포함합니다. § 15.7 부동소수점 평가를 참고하세요.

구간은 하한과 상한이 있는 연속된 숫자 집합입니다. 맥락에 따라 정수, 부동소수점, 실수, 또는 확장 실수 집합일 수 있습니다.

바닥 함수 표현확장 실수 x에 대해 정의됩니다:

천장 함수 표현확장 실수 x에 대해 정의됩니다:

절삭 함수확장 실수 x에 대해 정의됩니다:

올림 함수는 양의 정수 kn에 대해 다음과 같이 정의합니다:

전치c개의 열과 r개의 행을 갖는 행렬 A에 대해, r개의 열과 c개의 행을 갖는 행렬 AT로, A의 행을 AT의 열로 복사하여 만듭니다:

열 벡터의 전치는 해당 열 벡터를 1행 행렬로 해석하여 정의합니다. 동일하게, 행 벡터의 전치는 해당 행 벡터를 1열 행렬로 해석하여 정의합니다.

2. WGSL 모듈

WGSL 프로그램은 하나의 WGSL 모듈로 구성됩니다.

모듈은 선택적 디렉티브의 시퀀스 뒤에 모듈 범위 선언단언문이 이어집니다. 모듈은 다음과 같이 구성됩니다:

translation_unit :

global_directive * ( global_decl | global_assert | ';' ) *

global_decl :

global_variable_decl ';'

| global_value_decl ';'

| type_alias_decl ';'

| struct_decl

| function_decl

2.1. 셰이더 라이프사이클

WGSL 프로그램과 그 안에 포함될 수 있는 셰이더의 라이프사이클에는 네 가지 주요 이벤트가 있습니다. 처음 두 개는 WGSL 프로그램을 실행 준비하는 데 사용되는 WebGPU API 메서드에 해당합니다. 마지막 두 개는 셰이더의 실행 시작과 종료입니다.

이벤트는 다음과 같습니다:

  1. 셰이더 모듈 생성

    • 이는 WebGPU createShaderModule() 메서드가 호출될 때 발생합니다. WGSL 프로그램의 소스 텍스트가 이 시점에 제공됩니다.

  2. 파이프라인 생성

    • 이는 WebGPU createComputePipeline() 메서드 또는 WebGPU createRenderPipeline() 메서드가 호출될 때 발생합니다. 이들 메서드는 이전에 생성된 하나 이상의 셰이더 모듈과 기타 구성 정보를 함께 사용합니다.

    • 지정된 엔트리 포인트GPUProgrammableStage 셰이더를 구성하는 코드만 파이프라인 생성 시 고려됩니다. 즉, 엔트리 포인트와 관련 없는 코드는 컴파일 전 효과적으로 제거됩니다.

    • 참고:셰이더 단계는 별도로 컴파일된 것으로 간주되며, 따라서 모듈의 서로 다른 부분을 포함할 수 있습니다.

  3. 셰이더 실행 시작

  4. 셰이더 실행 종료

    • 이는 셰이더 내 모든 작업이 완료될 때 발생합니다:

      • 모든 인보케이션이 종료되고,

      • 모든 리소스 접근이 완료되고,

      • 출력이 있다면, 다음 파이프라인 단계로 전달됩니다.

이 이벤트들은 다음 때문에 순서가 지정됩니다:

2.2. 오류

WebGPU 구현체는 두 가지 이유로 셰이더 처리에 실패할 수 있습니다:

처리 오류는 셰이더 라이프사이클의 세 단계에서 발생할 수 있습니다:

참고: 예를 들어, 데이터 레이스는 탐지되지 않을 수 있습니다.

각 요구 사항 will은 가능한 한 빨리 확인됩니다. 즉:

맥락에서 명확하지 않은 경우, 이 명세서는 특정 요구 사항을 충족하지 못할 때 셰이더 생성 오류, 파이프라인 생성 오류, 또는 동적 오류 중 어떤 오류가 발생하는지 명시합니다.

오류의 결과는 다음과 같습니다:

2.3. 진단

구현체는 셰이더 모듈 생성 또는 파이프라인 생성 중에 진단을 생성할 수 있습니다. 진단은 구현체가 애플리케이션 작성자를 위해 생성하는 메시지입니다.

특정 조건이 만족되면 진단이 생성되거나 트리거됩니다. 이 조건을 트리거 규칙이라고 합니다. 조건이 만족된 소스 텍스트 내의 위치(지점 또는 범위)를 트리거 위치라고 합니다.

진단은 다음 속성을 가집니다:

심각도는 다음 중 하나이며, 높은 순서대로 나열됩니다:

오류

진단이 오류임을 의미합니다. 이는 셰이더 생성 오류 또는 파이프라인 생성 오류에 해당합니다.

경고

진단은 오류는 아니지만, 애플리케이션 개발자가 주목할 만한 이상 현상을 설명합니다.

정보

진단은 오류나 경고는 아니지만, 개발자가 주목해야 할 중요한 상황을 설명합니다.

꺼짐

진단이 비활성화되었습니다. 애플리케이션에 전달되지 않습니다.

트리거 규칙의 이름은 다음 중 하나입니다:

diagnostic_rule_name :

diagnostic_name_token

| diagnostic_name_token '.' diagnostic_name_token

2.3.1. 진단 처리

트리거된 진단은 다음과 같이 처리됩니다:

  1. 각 진단 D에 대해, D트리거 위치를 포함하고 동일한 트리거 규칙을 가진 가장 작은 진단 필터를 찾습니다.

    • 해당 필터가 있으면, D에 적용하여 D심각도를 갱신합니다.

    • 없으면 D는 변경되지 않습니다.

  2. 심각도가 꺼짐인 진단을 버립니다.

  3. 남은 진단 중 적어도 하나 DI정보 심각도를 가지면:

    • 동일한 트리거 규칙을 가진 다른 정보 진단은 일부 버려질 수 있으며, 원래 진단 DI만 남길 수 있습니다.

  4. 남은 진단 중 적어도 하나 DW경고 심각도를 가지면:

    • 동일한 트리거 규칙을 가진 다른 정보 또는 경고 진단은 일부 버려질 수 있으며, 원래 진단 DW만 남길 수 있습니다.

  5. 남은 진단 중 하나라도 오류 심각도를 가지면:

  6. 셰이더 모듈 생성 시점에 처리하는 경우, 남은 진단은 WebGPU messages 멤버에 저장됩니다. GPUCompilationInfo 객체입니다.

  7. 파이프라인 생성 중에 처리하는 경우, 오류 진단은 WebGPU GPUProgrammableStage 검증 시 실패로 이어집니다.

참고: 이 규칙들은 구현체가 오류를 발견하면 WGSL 모듈 처리 작업을 즉시 중단할 수 있도록 허용합니다. 또한, 경고에 대한 분석은 첫 번째 경고에서 중단될 수 있고, 정보 진단에 대한 분석도 첫 발생 시 중단될 수 있습니다. WGSL은 다양한 종류의 분석 수행 순서나 단일 분석 내 순서도 지정하지 않습니다. 따라서 동일한 WGSL 모듈에 대해 서로 다른 구현체가 동일한 심각도의 진단을 다르게 보고할 수 있습니다.

2.3.2. 필터링 가능한 트리거 규칙

대부분의 진단은 무조건 WebGPU 애플리케이션에 보고됩니다. 일부 종류의 진단은 필터링될 수 있으며, 그중 일부는 트리거 규칙의 이름을 지정하여 필터링할 수 있습니다. 다음 표는 필터링할 수 있는 표준 트리거 규칙 집합을 나열합니다.

필터링 가능한 진단 트리거 규칙
필터링 가능한 트리거 규칙 기본 심각도 트리거 위치 설명
derivative_uniformity 오류 모든 도함수 계산 내장 함수호출 위치. 즉, 다음 함수 호출의 위치: 내장 함수 호출이 도함수를 계산하지만, 균일성 분석에서 해당 호출이 균일 제어 흐름에서 발생함을 증명할 수 없는 경우입니다.

§ 15.2 균일성 참고.

subgroup_uniformity 오류 모든 서브그룹 또는 쿼드 내장 함수의 호출 위치. 서브그룹 또는 쿼드 내장 함수 호출이지만, 균일성 분석에서 해당 호출이 균일 제어 흐름에서 발생함을 증명할 수 없는 경우입니다. 또한, 균일성 분석에서 다음 파라미터 값이 균일하다는 것을 증명할 수 없는 경우도 포함됨:

§ 15.2 균일성 참고.

단일 diagnostic name-token으로 구성된 인식되지 않은 트리거 규칙을 사용하면 사용자 에이전트가 경고를 발생해야 합니다.

구현체는 여기 명시되지 않은 트리거 규칙도 지원할 수 있습니다. 단, diagnostic_rule_name의 다중 토큰 형식으로 철자가 되어야 합니다. 인식되지 않은 다중 토큰 형식의 트리거 규칙 사용은 자체적으로 진단을 트리거할 수 있습니다.

이 명세의 향후 버전에서는 특정 규칙을 제거하거나 기본 심각도를 완화(현재의 기본값을 더 약한 기본값으로 대체)할 수 있으며, 여전히 이전과의 호환성을 만족한 것으로 간주됩니다. 예를 들어, WGSL의 미래 버전에서는 derivative_uniformity의 기본 심각도를 오류에서 경고 또는 정보로 변경할 수 있습니다. 이러한 명세 변경 후에도 이전에 유효했던 프로그램은 계속 유효합니다.

2.3.3. 진단 필터링

필터링 가능한 진단트리거 규칙트리거되면, WGSL은 해당 진단을 버리거나 심각도를 수정할 수 있는 메커니즘을 제공합니다.

진단 필터 DF는 세 개의 매개변수를 가집니다:

진단 필터 DF(AR,NS,TR)를 진단 D에 적용하면 다음 효과가 있습니다:

범위 진단 필터진단 필터영향 범위가 소스 텍스트의 특정 범위인 것입니다. 범위 진단 필터는 아래 표에 명시된 대로, 영향 범위 시작에 @diagnostic 속성으로 지정됩니다. @diagnostic 속성은 다른 위치에 등장해서는 안 됩니다.

범위 진단 필터의 위치
위치 영향 범위
복합문의 시작 복합문 전체
함수 선언의 시작 함수 선언 전체
if 문의 시작 if 문 전체: if_clause와 관련된 모든 else_if_clause, else_clause 포함, 모든 조건식 포함
switch 문의 시작 switch 문 전체: 선택자 식 및 switch_body
switch_body의 시작 switch_body
loop 문의 시작 loop 문 전체
while 문의 시작 while 문 전체: 조건식과 반복 본문 모두
for 문의 시작 for 문 전체: for_header와 반복 본문
loop, while, for 루프의 루프 본문의 여는 중괄호('{') 바로 앞 루프 본문
continuing_compound_statement의 시작 continuing_compound_statement

참고: 다음도 복합문에 해당합니다: 함수 본문, case 절, default-alone 절, while, for 루프의 본문, if_clause, else_if_clause, else_clause의 본문.

예시: 텍스처 샘플링에 대한 범위 진단 필터
var<private> d: f32;
fn helper() -> vec4<f32> {
  // "if"의 본문에서 derivative_uniformity 진단을 비활성화합니다.
  if (d < 0.5) @diagnostic(off,derivative_uniformity) {
    return textureSample(t,s,vec2(0,0));
  }
  return vec4(0.0);
}

글로벌 진단 필터를 사용하면 전체 WGSL 모듈에 진단 필터를 적용할 수 있습니다.

예시: derivative_uniformity에 대한 글로벌 진단 필터
diagnostic(off,derivative_uniformity);
var<private> d: f32;
fn helper() -> vec4<f32> {
  if (d < 0.5) {
    // 글로벌 진단 필터에 의해 derivative_uniformity 진단이 비활성화됩니다.
    return textureSample(t,s,vec2(0,0));
  } else {
    // derivative_uniformity 진단의 심각도가 'warning'으로 설정됩니다.
    @diagnostic(warning,derivative_uniformity) {
      return textureSample(t,s,vec2(0,0));
    }
  }
  return vec4(0.0);
}

진단 필터 DF(AR1,NS1,TR1)와 DF(AR2,NS2,TR2)는 다음의 경우 충돌합니다:

진단 필터충돌해서는 안 됩니다.

참고: 글로벌 진단 필터는 충돌하지 않는 한 여러 개 허용됩니다.

WGSL의 진단 필터는 영향 범위가 완전히 중첩되도록 설계되었습니다. DF1의 영향 범위가 DF2와 겹치면, DF1의 영향 범위가 DF2에 완전히 포함되거나 그 반대입니다.

소스 위치 L와 트리거 규칙 TR에 대한 가장 가까운 포함 진단 필터는, 존재한다면 다음 조건을 만족하는 진단 필터 DF(AR,NS,TR)입니다:

영향 범위가 중첩되므로, 가장 가까운 포함 진단 필터는:

2.4. 제한

WGSL 구현체는 다음 제한을 충족하는 셰이더를 지원합니다. WGSL 구현체는 명시된 제한을 초과하는 셰이더도 지원할 수 있습니다.

참고: WGSL 구현체가 명시된 제한을 초과하는 셰이더를 지원하지 않으면 오류를 발생시켜야 합니다.

정량화된 셰이더 복잡도 제한
제한 최소 지원 값
구조체 타입의 최대 멤버 수 1023
합성 타입의 최대 중첩 깊이 15
함수 내 중괄호로 둘러싸인 문장의 최대 중첩 깊이 127
함수의 최대 파라미터255
switch 문에서 케이스 셀렉터 값의 최대 수. 각 case 문에 대해 case 값의 수의 합, default 절 포함. 1023
단일 셰이더에서 private 주소 공간에 인스턴스화되고 정적으로 접근되는 모든 변수의 최대 바이트 크기 8192
단일 함수에서 function 주소 공간에 선언되는 모든 변수의 최대 바이트 크기 8192
단일 셰이더에서 workgroup 주소 공간에 인스턴스화되고 정적으로 접근되는 모든 변수의 최대 바이트 크기

이 제한의 목적상, 고정 발자국 배열은 생성-고정 발자국 배열로 간주하여 override 값을 대입합니다.

이것은 WebGPU maxComputeWorkgroupStorageSize 제한을 독립적인 WGSL 제한으로 매핑한 것입니다.

16384
값 생성자 표현식에서 array 타입의 최대 요소 수 2047

3. 텍스트 구조

text/wgsl 미디어 타입은 WGSL 모듈의 콘텐츠임을 식별하는 데 사용됩니다. 부록 A: text/wgsl 미디어 타입을 참고하세요.

WGSL 모듈은 UTF-8 인코딩을 사용하는 유니코드 텍스트이며, 바이트 순서 표시(BOM)는 없습니다.

WGSL 모듈 텍스트는 연속된 비어 있지 않은 유니코드 코드 포인트들의 시퀀스로 구성되며, 다음과 같이 그룹화됩니다:

프로그램 텍스트에는 null 코드 포인트(U+0000)가 포함되어서는 안 됩니다.

3.1. 파싱

WGSL 모듈을 파싱하려면:

  1. 주석을 제거합니다:

    • 첫 번째 주석을 공백 코드 포인트(U+0020)로 대체합니다.

    • 주석이 더 이상 남지 않을 때까지 반복합니다.

  2. 템플릿 리스트를 찾습니다. 알고리즘§ 3.9 Template Lists를 참고하세요.

  3. 전체 텍스트를 파싱하여 translation_unit 문법 규칙과 일치하는지 시도합니다. 파싱은 LALR(1) 파서(토큰 하나 lookahead)를 사용하며 [DeRemer1969], 다음과 같은 커스터마이즈가 적용됩니다:

    • 토큰화는 파싱과 병행되며, 컨텍스트 인식형입니다. 파서가 다음 토큰을 요청할 때:

      • 초기 연속 공백 코드 포인트들을 소비하고 무시합니다.

      • 다음 코드 포인트가 템플릿 리스트의 시작이면, 이를 소비하고 _template_args_start를 반환합니다.

      • 다음 코드 포인트가 템플릿 리스트의 끝이면, 이를 소비하고 _template_args_end를 반환합니다.

      • 그 외의 경우:

        • 토큰 후보란 남은 미소비 코드 포인트의 비어 있지 않은 접두사로 형성된 WGSL 토큰입니다.

        • 반환되는 토큰은 현재 파서 상태에서 유효한 lookahead 토큰이면서 가장 긴 토큰 후보입니다. [VanWyk2007]

다음 경우 셰이더 생성 오류가 발생합니다:

3.2. 공백 및 줄바꿈

공백은 유니코드 Pattern_White_Space 속성의 코드 포인트들 중 하나 이상으로 구성된 조합입니다. Pattern_White_Space에 속하는 코드 포인트 집합은 다음과 같습니다:

줄바꿈공백 코드 포인트의 연속으로, 줄의 끝을 나타냅니다. UAX14 섹션 6.1 Non-tailorable Line Breaking Rules의 "mandatory break"를 신호하는 공백으로 정의되며, LB4LB5를 참고하세요. 즉, 줄바꿈은 다음 중 하나입니다:

참고: 소스 텍스트 위치를 줄 번호로 보고하는 진단은 줄 개수를 셀 때 줄바꿈을 사용해야 합니다.

3.3. 주석

주석은 WGSL 프로그램의 유효성이나 의미에 영향을 주지 않는 텍스트 영역이며, 주석은 토큰을 분리할 수 있습니다. 셰이더 작성자는 프로그램을 문서화하기 위해 주석을 사용할 수 있습니다.

줄 끝 주석주석의 한 종류로, 두 코드 포인트 // (U+002FU+002F)와 그 뒤에 오는 코드 포인트들로 구성되며, 다음 중 하나가 나오기 전까지입니다:

블록 주석주석의 한 종류로, 다음으로 구성됩니다:

참고: 블록 주석은 중첩될 수 있습니다. 블록 주석은 시작과 끝 텍스트 시퀀스가 반드시 일치해야 하며, 임의의 중첩을 허용하기 때문에 정규표현식으로 인식할 수 없습니다. 이는 정규 언어의 펌핑 렘마(Pumping Lemma)로 인한 결과입니다.

예시: 주석
const f = 1.5; // 이것은 줄 끝 주석입니다.
const g = 2.5; /* 이것은 여러 줄에 걸친 블록 주석입니다
                블록 주석은 중첩될 수 있습니다.
                /* 블록 주석은 중첩 가능합니다.
                 */
                모든 블록 주석은 반드시 종료되어야 합니다.
               */

3.4. 토큰

토큰은 연속된 코드 포인트 시퀀스로 다음 중 하나를 형성합니다:

3.5. 리터럴

리터럴은 다음 중 하나입니다:

literal :

int_literal

| float_literal

| bool_literal

3.5.1. 불리언 리터럴

예시: 불리언 리터럴
const a = true;
const b = false;
bool_literal :

'true'

| 'false'

3.5.2. 숫자 리터럴

숫자 리터럴의 형태는 패턴 매칭을 통해 정의됩니다.

정수 리터럴은 다음과 같습니다:

참고: 0이 아닌 정수 리터럴 앞에 0이 오는 것(예: 012)은 금지되며, 이는 다른 언어에서 0으로 시작하면 8진수로 인식되는 혼동을 방지하기 위함입니다.

int_literal :

decimal_int_literal

| hex_int_literal

decimal_int_literal :

/0[iu]?/

| /[1-9][0-9]*[iu]?/

예시: 십진수 정수 리터럴
const a = 1u;
const b = 123;
const c = 0;
const d = 0i;
hex_int_literal :

/0[xX][0-9a-fA-F]+[iu]?/

예시: 16진수 정수 리터럴
const a = 0x123;
const b = 0X123u;
const c = 0x3f;

부동소수점 리터럴십진수 부동소수점 리터럴 또는 16진수 부동소수점 리터럴입니다.

float_literal :

decimal_float_literal

| hex_float_literal

부동소수점 리터럴은 두 개의 논리적 부분을 가집니다: 분수를 나타내는 유효숫자와 선택적인 지수입니다. 대략적으로, 리터럴의 값은 유효숫자에 기저값을 지수만큼 거듭제곱한 값을 곱한 것입니다. 유효숫자 자리수는 유효숫자라고 하며, 0이 아니거나, 해당 숫자의 왼쪽과 오른쪽에 모두 0이 아닌 유효숫자가 있을 때 유효숫자입니다. 유효숫자는 왼쪽부터 오른쪽으로 센다: N번째 유효숫자는 왼쪽에 N-1개의 유효숫자가 있습니다.

십진수 부동소수점 리터럴은 다음과 같습니다:

decimal_float_literal :

/0[fh]/

| /[1-9][0-9]*[fh]/

| /[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?[fh]?/

| /[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?[fh]?/

| /[0-9]+[eE][+-]?[0-9]+[fh]?/

예시: 십진수 부동소수점 리터럴
const a = 0.e+4f;
const b = 01.;
const c = .01;
const d = 12.34;
const f = .0f;
const g = 0h;
const h = 1e-3;
십진수 부동소수점 리터럴의 수학적 값은 다음과 같이 계산됩니다:

참고: 십진수 유효숫자는 20자리 이후로 잘려, 약 log(10)/log(2)×20 ≈ 66.4 비트가 보존됩니다.

16진수 부동소수점 리터럴은 다음과 같습니다:

hex_float_literal :

/0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9]+[fh]?)?/

| /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*([pP][+-]?[0-9]+[fh]?)?/

| /0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?/

예시: 16진수 부동소수점 리터럴
const a = 0xa.fp+2;
const b = 0x1P+4f;
const c = 0X.3;
const d = 0x3p+2h;
const e = 0X1.fp-4;
const f = 0x3.2p+2h;
16진수 부동소수점 리터럴의 수학적 값은 다음과 같이 계산됩니다:

참고: 16진수 유효숫자는 16자리 이후로 잘려, 약 4 ×16 = 64 비트가 보존됩니다.

숫자 리터럴에 접미사가 있으면 해당 리터럴은 특정 구체적 스칼라 타입의 값을 나타냅니다. 그렇지 않으면 아래 정의된 추상 숫자 타입 중 하나의 값을 나타냅니다. 두 경우 모두, 리터럴이 나타내는 값은 대상 타입으로 변환된 수학적 값이며, 변환 규칙은 § 15.7.6 부동소수점 변환을 따릅니다.

숫자 리터럴의 타입 매핑
숫자 리터럴 접미사 타입 예시
정수 리터럴 i i32 42i
정수 리터럴 u u32 42u
정수 리터럴 AbstractInt 124
부동소수점 리터럴 f f32 42f 1e5f 1.2f 0x1.0p10f
부동소수점 리터럴 h f16 42h 1e5h 1.2h 0x1.0p10h
부동소수점 리터럴 AbstractFloat 1e5 1.2 0x1.0p10

셰이더 생성 오류는 다음 경우 발생합니다:

참고: 16진수 float 값 0x1.00000001p0은 정확히 표현하려면 33 유효숫자 비트가 필요하지만, f32는 명시적 유효숫자 비트가 23개뿐입니다.

참고: 16진수 float 리터럴에 f 접미사를 붙여 타입을 강제하려면 반드시 이진 지수도 사용해야 합니다. 예시: 0x1p0f로 작성. 0x1f는 16진수 정수 리터럴로 취급됨.

3.6. 키워드

키워드는 미리 정의된 언어 개념을 참조하는 토큰입니다. WGSL 키워드 목록은 § 16.1 키워드 요약을 참고하세요.

3.7. 식별자

식별자는 이름으로 사용되는 토큰의 한 종류입니다. 자세한 내용은 § 5 선언과 범위를 참고하세요.

WGSL은 사용 사례를 구분하기 위해 두 개의 문법 비종결(nonterminal)을 사용합니다:

ident :

ident_pattern_token _disambiguate_template

member_ident :

ident_pattern_token

식별자의 형식은 Unicode Standard Annex #31(Unicode Version 14.0.0 기준)에 따라 정의되며, 아래와 같은 추가 설명이 있습니다.

식별자는 다음과 같이 UAX31 문법 기준 프로필을 사용합니다:

<Identifier> := <Start> <Continue>* (<Medial> <Continue>+)*

<Start> := XID_Start + U+005F
<Continue> := <Start> + XID_Continue
<Medial> :=

즉, 다음과 같이 비ASCII 코드 포인트가 포함된 식별자도 유효합니다: Δέλτα, réflexion, Кызыл, 𐰓𐰏𐰇, 朝焼け, سلام, 검정, שָׁלוֹם, गुलाबी, փիրուզ.

단, 다음 예외가 있습니다:

ident_pattern_token :

/([_\p{XID_Start}][\p{XID_Continue}]+)|([\p{XID_Start}])/u

Unicode Character Database for Unicode Version 14.0.0에는 XID_StartXID_Continue의 모든 유효 코드 포인트의 비공식 목록이 포함되어 있습니다.

참고: 일부 내장 함수반환 타입은 이름을 WGSL 소스에서 사용할 수 없는 구조체 타입입니다. 이러한 구조체 타입은 두 개의 언더스코어로 시작하는 이름이 선언된 것처럼 설명됩니다. 결과 값은 새로운 let 또는 var에 타입 추론을 이용해 저장하거나, 바로 멤버 이름으로 추출할 수 있습니다. frexpmodf 설명의 예시 사용을 참고하세요.

3.7.1. 식별자 비교

두 WGSL 식별자가 동일하다는 것은, 오직 두 식별자가 동일한 코드 포인트 시퀀스로 구성되어 있을 때만입니다.

참고: 이 명세는 비교 목적으로 유니코드 정규화를 허용하지 않습니다. 시각적으로/의미적으로 동일하지만 다른 유니코드 문자 시퀀스를 사용하는 값은 일치하지 않습니다. 콘텐츠 작성자는 값 선택 시 동일한 인코딩 시퀀스를 일관되게 사용하거나 문제가 될 수 있는 문자를 피할 것을 권장합니다. 자세한 내용은 [CHARMOD-NORM] 참고.

참고: 사용자 에이전트는 WGSL 모듈의 의미가 동일 식별자의 모든 인스턴스를 해당 식별자의 동형(homograph)으로 치환할 때 달라질 경우, 개발자에게 경고를 표시해야 합니다. (동형문자(homoglyph)는 코드 포인트 시퀀스가 독자에게 다른 시퀀스와 동일하게 보일 수 있는 경우를 의미합니다. 동형문자 탐지 예시로는 앞 문단에 언급된 변환, 매핑, 매칭 알고리즘 등이 있습니다. 두 코드 포인트 시퀀스는 하나의 시퀀스를 반복적으로 동형문자로 치환하여 다른 시퀀스로 만들 수 있으면 동형문자입니다.)

3.8. 상황에 따라 달라지는 이름

상황에 따라 달라지는 이름은 특정 문법적 맥락에서만 개념에 이름을 붙일 때 사용하는 토큰입니다. 이 토큰의 철자는 식별자와 같을 수 있지만, 해당 토큰은 선언된 객체로 해결되지 않습니다. 이 섹션에서는 상황에 따라 달라지는 이름으로 사용되는 토큰을 나열합니다. 해당 토큰은 키워드예약어이면 안 됩니다.

3.8.1. 속성 이름

§ 12 속성 참고.

속성 이름은 다음과 같습니다:

3.8.2. 내장 값 이름

내장 값 이름 토큰토큰으로, 내장 값의 이름에 사용됩니다.

§ 13.3.1.1 내장 입력 및 출력를 참고하세요.

builtin_value_name :

ident_pattern_token

내장 값 이름은 다음과 같습니다:

3.8.3. 진단 규칙 이름

진단 이름 토큰토큰으로, 진단 트리거 규칙의 이름에 사용됩니다.

§ 2.3 진단을 참고하세요.

diagnostic_name_token :

ident_pattern_token

미리 정의된 진단 규칙 이름은 다음과 같습니다:

3.8.4. 진단 심각도 제어 이름

유효한 진단 필터 심각도 제어 이름은 § 2.3 진단에 나열되어 있지만, 일반적으로 식별자와 동일한 형식을 가집니다:

severity_control_name :

ident_pattern_token

진단 필터 심각도 제어 이름은 다음과 같습니다:

3.8.5. 확장 이름

유효한 enable-extension 이름은 § 4.1.1 확장 활성화에 나열되어 있지만, 일반적으로 식별자와 동일한 형식을 가집니다:

enable_extension_name :

ident_pattern_token

enable-extension 이름은 다음과 같습니다:

유효한 언어 확장 이름은 § 4.1.2 언어 확장에 나열되어 있지만, 일반적으로 식별자와 동일한 형식을 가집니다:

language_extension_name :

ident_pattern_token

언어 확장 이름은 다음과 같습니다:

3.8.6. 보간 타입 이름

보간 타입 이름 토큰토큰으로, 보간 타입 (interpolate_type_name)의 이름에 사용됩니다.

§ 13.3.1.4 보간을 참고하세요.

보간 타입 이름은 다음과 같습니다:

3.8.7. 보간 샘플링 이름

보간 샘플링 이름 토큰토큰으로, 보간 샘플링의 이름에 사용됩니다.

§ 13.3.1.4 보간을 참고하세요.

interpolate_sampling_name :

ident_pattern_token

보간 샘플링 이름은 다음과 같습니다:

3.8.8. 스위즐 이름

스위즐 이름은 벡터 접근 식에서 사용됩니다:

swizzle_name :

/[rgba]/

| /[rgba][rgba]/

| /[rgba][rgba][rgba]/

| /[rgba][rgba][rgba][rgba]/

| /[xyzw]/

| /[xyzw][xyzw]/

| /[xyzw][xyzw][xyzw]/

| /[xyzw][xyzw][xyzw][xyzw]/

3.9. 템플릿 리스트

템플릿 매개변수화는 일반 개념을 수정하는 매개변수를 지정하는 방법입니다. 템플릿 매개변수화를 작성하려면 일반 개념을 쓴 뒤 템플릿 리스트를 씁니다.

주석공백을 무시하면, 템플릿 리스트란 다음과 같습니다:

템플릿 매개변수의 형식은 아래 템플릿 리스트 발견 알고리즘에 의해 암시적으로 정의됩니다. 일반적으로 이름, 식, 또는 타입입니다.

참고: 예를 들어 vec3<f32>vec3라는 일반 개념을 <f32>라는 템플릿 리스트(매개변수 하나, f32 타입)로 수정한 매개변수화입니다. vec3<f32>는 구체적인 벡터 타입을 뜻합니다.

참고: 예를 들어 var<storage,read_write>는 일반 var 개념을 storage, read_write 템플릿 매개변수로 수정합니다.

참고: 예를 들어 array<vec4<f32>>는 두 번의 템플릿 매개변수화가 있습니다:

템플릿 리스트를 구분하는 '<' (U+003C)와 '>' (U+003E) 코드 포인트는 다음에도 사용됩니다:

구문적 모호함은 템플릿 리스트 쪽으로 해결됩니다:

템플릿 리스트 발견 알고리즘은 아래와 같습니다. 다음과 같은 가정과 특성을 사용합니다:

  1. 템플릿 매개변수이며, 따라서 '<'(U+003C) 또는 '='(U+003D)로 시작하지 않습니다.

  2. 식에는 ';'(U+003B), '{'(U+007B), ':'(U+003A) 코드 포인트가 포함되지 않습니다.

  3. 식에는 대입문이 포함되지 않습니다.

  4. '='(U+003D) 코드 포인트는 비교 연산('<=', '>=', '==', '!=')의 일부로만 나타납니다. 그 외의 경우 '='는 대입문의 일부입니다.

  5. 템플릿 리스트 구분자는 괄호 '(...)'와 배열 인덱싱 '[...]'로 구성된 중첩 식을 존중합니다. 템플릿 리스트의 시작과 끝은 동일한 중첩 레벨에 있어야 합니다.

알고리즘: 템플릿 리스트 발견

입력: 프로그램 소스 텍스트.

레코드 타입:

UnclosedCandidate는 다음을 포함하는 레코드 타입입니다:

TemplateList는 다음을 포함하는 레코드 타입입니다:

출력: DiscoveredTemplateLists: TemplateList 레코드의 리스트.

절차:

참고:아래와 같이 알고리즘을 수정하면 템플릿 매개변수의 소스 범위를 찾을 수 있습니다:

참고: 알고리즘은 리터럴을 명시적으로 건너뜁니다. 왜냐하면 일부 숫자 리터럴은 마지막에 문자가 올 수 있는데, 예를 들어 1.0f처럼 끝나는 fident_pattern_token의 시작으로 오인하지 않아야 하기 때문입니다.

참고: A ( B < C, D > ( E ) )라는 구에서는 < C, D >템플릿 리스트입니다.

참고: 알고리즘은 식 중첩을 존중합니다. 특정 템플릿 리스트의 시작과 끝은 서로 다른 식 중첩 레벨에 올 수 없습니다. 예를 들어 array<i32,select(2,3,a>b)>에서 템플릿 리스트는 세 개의 매개변수를 가지며, 마지막 매개변수는 select(2,3,a>b)입니다. a>b'>'select 함수 호출식의 괄호 내부에 있으므로 템플릿 리스트를 종료하지 않습니다.

참고: 템플릿 리스트의 양 끝은 동일한 인덱싱 식 내에 있어야 합니다. 예를 들어 a[b<d]>()는 유효한 템플릿 리스트를 포함하지 않습니다.

참고: A<B<<C>에서 B<<CB 다음에 왼쪽 시프트 연산자 '<<'C가 이어진 것으로 파싱됩니다. 템플릿 발견 알고리즘은 B를 보고 '<'(U+003C)를 본 다음, 그 뒤의 '<'(U+003C)가 템플릿 인수 시작이 될 수 없음을 알게 되어, B 바로 뒤의 '<'는 템플릿 리스트 시작이 아닙니다. 처음 '<'와 마지막 '>'만 템플릿 리스트 구분자이며, 매개변수는 B<<C입니다.

참고: A<B<=C>도 위와 비슷하게 분석되어, B<=CB 다음에 작거나 같은 연산자 '<='C가 이어진 것으로 파싱됩니다. 템플릿 발견 알고리즘은 B를 보고 '<'(U+003C)를 본 다음, 그 뒤의 '='(U+003D)가 템플릿 인수 시작이 될 수 없음을 알게 되어, B 바로 뒤의 '<'는 템플릿 리스트 시작이 아닙니다. 처음 '<'와 마지막 '>'만 템플릿 리스트 구분자이며, 매개변수는 B<=C입니다.

참고: A<(B>=C)>를 검사할 때, 하나의 템플릿 리스트가 있으며 처음 '<'(U+003C)에서 마지막 '>'(U+003E)까지이고, 템플릿 인수는 B>=C입니다. B 이후 첫 번째 '>'(U+003C) 코드 포인트를 검사한 후, '='(U+003D) 코드 포인트는 대입이 아니도록 특별히 인식해야 합니다.

참고: A<(B!=C)>를 검사할 때, 하나의 템플릿 리스트가 있으며 처음 '<'(U+003C)에서 마지막 '>'(U+003E)까지이고, 템플릿 인수는 B!=C입니다. 'B' 이후 '!'(U+0021) 코드 포인트를 검사한 후, '='(U+003D) 코드 포인트는 대입이 아니도록 특별히 인식해야 합니다.

참고: A<(B==C)>를 검사할 때, 하나의 템플릿 리스트가 있으며 처음 '<'(U+003C)에서 마지막 '>'(U+003E)까지이고, 템플릿 인수는 B==C입니다. 'B' 이후 첫 번째 '='(U+003D) 코드 포인트를 검사한 후, 두 번째 '='(U+003D) 코드 포인트도 대입이 아니도록 특별히 인식해야 합니다.

템플릿 리스트 발견이 완료되면, 파싱에서 각 템플릿 리스트가 template_list 문법 규칙에 맞는지 검사합니다.

template_list :

_template_args_start template_arg_comma_list _template_args_end

template_arg_comma_list :

template_arg_expression ( ',' template_arg_expression ) * ',' ?

template_arg_expression :

expression

4. 지시문

지시문토큰 시퀀스로, WGSL 프로그램이 WebGPU 구현에서 처리되는 방식을 수정합니다.

지시문은 선택 사항입니다. 만약 존재한다면, 모든 지시문은 반드시 모든 선언 또는 const 단언문 이전에 나타나야 합니다.

global_directive :

diagnostic_directive

| enable_directive

| requires_directive

4.1. 확장

WGSL은 시간이 지남에 따라 발전할 것으로 예상됩니다.

확장은 WGSL 명세의 일관된 수정 집합을 명명하여 그룹화한 것으로, 다음과 같은 내용을 포함할 수 있습니다:

가상적으로, 확장은 다음을 할 수 있습니다:

확장에는 두 종류가 있습니다: enable-extensionslanguage extensions입니다.

4.1.1. Enable Extension

enable-extension확장으로, 다음의 조건을 모두 만족해야 기능을 사용할 수 있습니다:

Enable-extension은 하드웨어 기능 중 보편적으로 제공되지 않는 부분을 노출하는 것을 목적으로 합니다.

enable directive는 하나 이상의 enable-extension 기능을 활성화하는 지시문입니다. 만약 구현체가 나열된 모든 enable-extension을 지원하지 않으면 셰이더 생성 오류가 발생합니다.

enable_directive :

'enable' enable_extension_list ';'

enable_extension_list :

enable_extension_name ( ',' enable_extension_name ) * ',' ?

다른 지시문과 마찬가지로, enable directive가 존재한다면, 반드시 모든 선언const 단언문 이전에 나타나야 합니다. 확장 이름은 식별자가 아닙니다: 선언으로 해석되지 않습니다.

유효한 enable-extension 목록은 다음 표에 나와 있습니다.

Enable-extensions
WGSL enable-extension WebGPU GPUFeatureName 설명
f16 "shader-f16" f16 타입을 WGSL 모듈에서 사용할 수 있습니다. 그렇지 않으면 f16을(직접 또는 간접적으로) 사용하면 셰이더 생성 오류가 발생합니다.
clip_distances "clip-distances" clip_distances 내장 변수를 WGSL 모듈에서 사용할 수 있습니다. 그렇지 않으면 clip_distances를 사용하면 셰이더 생성 오류가 발생합니다.
dual_source_blending "dual-source-blending" blend_src 속성을 WGSL 모듈에서 사용할 수 있습니다. 그렇지 않으면 blend_src를 사용하면 셰이더 생성 오류가 발생합니다.
subgroups "subgroups" subgroup 내장 값, subgroup 내장 함수, 그리고 quad 내장 함수를 WGSL 모듈에서 사용할 수 있습니다. 그렇지 않으면 해당 항목을 사용하면 셰이더 생성 오류가 발생합니다.
primitive_index "primitive-index" primitive_index 내장 변수를 WGSL 모듈에서 사용할 수 있습니다. 그렇지 않으면 primitive_index를 사용하면 셰이더 생성 오류가 발생합니다.
예시: 가상 enable-extension 사용
// 임의 정밀도의 부동 소수점 타입용 가상 확장 활성화.
enable arbitrary_precision_float;
enable arbitrary_precision_float; // 중복된 enable 지시문도 허용됩니다.

// 반올림 모드 제어를 위한 가상 확장 활성화.
enable rounding_mode;

// arbitrary_precision_float 확장으로 아래 항목 사용 가능:
//    - 타입 f<E,M>
//    - 함수 반환 타입, 형식 매개변수, let 선언에서 타입으로 사용
//    - AbstractFloat로부터 값 생성자 사용 가능
//    - 나눗셈 연산자 /의 피연산자로 사용 가능
// rounding_mode enable 지시문으로 @rounding_mode 속성 사용 가능.
@rounding_mode(round_to_even)
fn halve_it(x : f<8, 7>) -> f<8, 7> {
  let two = f<8, 7>(2);
  return x / 2; // round to even 반올림 모드 사용
}

4.1.2. 언어 확장

언어 확장은 구현체가 지원한다면 자동으로 사용할 수 있는 확장입니다. 프로그램에서 명시적으로 요청할 필요가 없습니다.

언어 확장은 어떤 WebGPU 구현에서도 합리적으로 지원될 수 있는 기능을 구현합니다. 만약 해당 기능이 보편적으로 제공되지 않는다면, 이는 일부 WebGPU 구현체가 아직 해당 기능을 구현하지 않았기 때문입니다.

참고: 예를 들어, do-while 반복문은 언어 확장이 될 수 있습니다.

WebGPU wgslLanguageFeatures 멤버는 GPU 객체에 존재하며, 구현체가 지원하는 언어 확장 목록을 제공합니다.

requires-directive 는 프로그램이 하나 이상의 언어 확장을 사용함을 문서화하는 지시문입니다. 이는 구현체가 노출하는 기능을 변경하지 않습니다. 구현체가 요구된 확장 중 하나라도 지원하지 않으면 셰이더 생성 오류가 발생합니다.

WGSL 모듈은 requires-directive를 사용하여 이식성 문제 가능성과 의도된 최소 이식성 기준을 알릴 수 있습니다.

참고: WebGPU 구현 외부의 도구는 프로그램이 사용하는 모든 언어 확장이 프로그램 내 requires-directive에 포함되어 있는지 확인할 수 있습니다.

requires_directive :

'requires' language_extension_list ';'

language_extension_list :

language_extension_name ( ',' language_extension_name ) * ',' ?

다른 지시문과 마찬가지로, requires-directive가 존재한다면, 모든 선언const 단언문 이전에 나타나야 합니다. 확장 이름은 식별자가 아닙니다: 선언으로 해석되지 않습니다.

언어 확장
WGSL 언어 확장 설명
readonly_and_readwrite_storage_textures readread_write access modestorage texture에 사용할 수 있습니다. 또한 textureBarrier 내장 함수가 추가됩니다.
packed_4x8_integer_dot_product 32비트 정수 스칼라에 4-컴포넌트 8비트 정수 벡터를 패킹하여, dot4U8Packeddot4I8Packed 내장 함수의 도트 곱 연산에 입력으로 사용할 수 있습니다. 또한 pack4xI8, pack4xU8, pack4xI8Clamp, pack4xU8Clamp, unpack4xI8, unpack4xU8 내장 함수로 4-컴포넌트 8비트 정수 벡터를 패킹/언패킹할 수 있습니다.
unrestricted_pointer_parameters 제한 사항 중에서 사용자 정의 함수에 대한 다음 제한을 제거합니다:

사용자 정의 함수에서 포인터 타입 매개변수는 반드시 다음 주소 공간 중 하나여야 합니다:

사용자 정의 함수의 포인터 타입 인자는 그 root identifier와 동일한 memory view를 가져야 합니다.

pointer_composite_access 복합 값 분해 표현식에서 루트 표현이 포인터인 경우 참조를 반환하는 것을 지원합니다.

예를 들어, p가 멤버 m을 가진 구조체에 대한 포인터라면, p.mp가 가리키는 구조체 내 m의 메모리 위치에 대한 참조입니다.

마찬가지로, pa가 배열에 대한 포인터라면, pa[i]pa가 가리키는 배열의 i번째 요소 메모리 위치에 대한 참조입니다.

참고: WGSL은 시간이 지남에 따라 당시에 일반적으로 지원되는 언어 확장 기능을 모두 포함하는 언어 확장을 정의할 의도를 가지고 있습니다. requires-directive에서는 이러한 확장을 모든 공통 기능을 나열하는 약어로 사용할 수 있습니다. 이는 점진적으로 기능 집합이 증가하는 것을 나타내며, 일종의 언어 버전처럼 볼 수 있습니다.

4.2. 글로벌 진단 필터

글로벌 진단 필터영향 범위가 WGSL 모듈 전체에 해당하는 진단 필터입니다. 지시문이므로, 모든 모듈 범위 선언 이전에 나타납니다. 속성 형태와 동일하게 작성되지만 앞의 @(U+0040) 코드 포인트 없이, 마지막에 세미콜론을 붙입니다.

diagnostic_directive :

'diagnostic' diagnostic_control ';'

5. 선언과 범위

선언식별자와 아래 객체 중 하나를 연결합니다:

즉, 선언은 객체에 대한 이름을 도입합니다.

선언이 프로그램 소스에 나타나지만 다른 선언의 본문 밖에 있다면 모듈 범위에 있습니다.

함수 선언은 모듈 범위에 나타납니다. 함수 선언은 형식 매개변수 선언을 포함할 수 있고, 변수와 값 선언본문 내부에 포함할 수 있습니다. 이러한 선언들은 모듈 범위에 있지 않습니다.

참고: 다른 선언을 포함할 수 있는 선언은 함수 선언만 있습니다.

일부 객체는 WebGPU 구현에서 제공되며, WGSL 모듈 소스가 시작되기 전에 선언된 것으로 간주됩니다. 이러한 객체를 사전 선언됨이라 합니다. 예를 들어, WGSL은 다음을 사전 선언합니다:

범위란 선언된 식별자가 연관된 객체를 가리킬 수 있는 프로그램 소스 위치 집합입니다. 이러한 위치에서 식별자가 해당 선언의 범위 내에 있음이라고 합니다.

선언이 나타나는 위치에 따라 범위가 결정됩니다:

같은 WGSL 소스 프로그램에서 두 선언은 동시에 다음을 허용하지 않아야 합니다:

참고: 사전 선언 객체는 WGSL 소스 내에 선언이 없습니다. 따라서 사용자가 모듈 범위나 함수 내부에서 같은 이름의 선언을 할 수 있습니다.

식별자는 문법적 맥락에 따라 다음과 같이 사용됩니다:

ident 토큰이 다른 곳에 선언된 객체를 나타내는 이름으로 사용될 때, 반드시 어떤 선언의 범위 내에 있어야 합니다. 식별자 토큰이 가리키는 객체는 다음과 같이 결정됩니다:

위 알고리즘으로 식별자를 선언에 대응시킬 때, 식별자가 해당 선언으로 해석됨이라고 합니다. 또한 식별자가 선언된 객체로 해석됨이라고도 합니다.

모든 모듈 범위 선언이 재귀적인 경우 셰이더 생성 오류입니다. 즉, 선언들 사이에 순환(cycle)이 존재하면 안 됩니다:

다음과 같은 방향 그래프를 생각합니다:

이 그래프에는 순환이 없어야 합니다.

참고: 함수 본문함수 선언의 일부이므로, 함수는 직접적 또는 간접적으로 재귀적이어서는 안 됩니다.

참고: 모듈 범위가 아닌(non-module scope) 식별자 선언은 반드시 사용보다 먼저 텍스트에 나타나야 합니다.

예시: 올바른 선언과 잘못된 선언
// 올바름: 사용자 정의 변수는 내장 함수와 같은 이름을 가질 수 있음.
var<private> modf: f32 = 0.0;

// 올바름: foo_1은 프로그램 전체에서 범위 내.
var<private> foo: f32 = 0.0; // foo_1

// 올바름: bar_1은 프로그램 전체에서 범위 내.
var<private> bar: u32 = 0u; // bar_1

// 올바름: my_func_1은 프로그램 전체에서 범위 내.
// 올바름: foo_2는 함수 끝까지 범위 내.
fn my_func(foo: f32) { // my_func_1, foo_2
  // 'foo'에 대한 참조는 모두 함수 매개변수를 해석함.

  // 잘못됨: modf는 모듈 범위 변수로 해석됨.
  let res = modf(foo);

  // 잘못됨: foo_2의 범위는 함수 끝에서 끝남.
  var foo: f32; // foo_3

  // 올바름: bar_2는 함수 끝까지 범위 내.
  var bar: u32; // bar_2
  // 'bar'에 대한 참조는 bar_2로 해석함.
  {
    // 올바름: foo_4는 복합문 끝까지 범위 내.
    var foo : f32; // foo_4

    // 올바름: bar_3는 복합문 끝까지 범위 내.
    var bar: u32; // bar_3
    // 'bar'에 대한 참조는 bar_3로 해석함.

    // 잘못됨: bar_4는 bar_3과 같은 범위 끝을 가짐.
    var bar: i32; // bar_4

    // 올바름: i_1은 for 루프 끝까지 범위 내.
    for ( var i: i32 = 0; i < 10; i++ ) { // i_1
      // 잘못됨: i_2는 i_1과 같은 범위 끝을 가짐.
      var i: i32 = 1; // i_2.
    }
  }

  // 잘못됨: bar_5는 bar_2와 같은 범위 끝을 가짐.
  var bar: u32; // bar_5

  // 올바름: later_def(모듈 범위 선언)은 프로그램 전체에서 범위 내.
  var early_use : i32 = later_def;
}

// 잘못됨: bar_6은 bar_1과 같은 범위를 가짐.
var<private> bar: u32 = 1u; // bar_6

// 잘못됨: my_func_2는 my_func_1과 같은 범위 끝을 가짐.
fn my_func() { } // my_func_2

// 올바름: my_foo_1은 프로그램 전체에서 범위 내.
fn my_foo( //my_foo_1
  // 올바름: my_foo_2는 함수 끝까지 범위 내.
  my_foo: i32 // my_foo_2
) { }

var<private> later_def : i32 = 1;
예시: 사전 선언 객체 가리기(shadowing)
// 이 선언은 사전 선언된 'min' 내장 함수를 가립니다.
// 이 선언은 모듈 범위에 있으므로 전체 소스에서 범위 내입니다.
// 내장 함수는 더이상 접근할 수 없습니다.
fn min() -> u32 { return 0; }

const rgba8unorm = 12; // 이 선언은 사전 선언된 'rgba8unorm' 열거자를 가립니다.

6. 타입

프로그램은 값을 계산합니다.

WGSL에서 타입은 값의 집합이며, 각 값은 정확히 하나의 타입에 속합니다. 값의 타입은 해당 값에 대해 수행할 수 있는 연산의 문법과 의미를 결정합니다.

예를 들어, 수학적 숫자 1은 WGSL에서 다음과 같은 개별 값에 해당합니다:

WGSL은 이 값들을 서로 다르게 취급하는데, 이는 기계적 표현과 연산이 다르기 때문입니다.

타입은 사전 선언됨이거나, WGSL 소스에서 선언을 통해 생성됩니다.

일부 타입은 템플릿 파라미터화로 표현됩니다. 타입 생성자사전 선언된 객체로, 템플릿 리스트와 결합될 때 타입을 나타냅니다. 예를 들어, atomic<u32> 타입은 타입 생성자 atomic과 템플릿 리스트 <u32>를 결합한 것입니다.

타입의 개념과 WGSL에서 해당 타입을 나타내는 문법을 구분합니다. 많은 경우 이 명세에서 타입의 표기법은 WGSL 문법과 동일합니다. 예를 들어:

일부 WGSL 타입은 소스 프로그램을 분석하거나 프로그램의 런타임 동작을 결정하는 데만 사용됩니다. 이러한 타입은 명세에서 설명하지만 WGSL 소스 텍스트에는 나타나지 않습니다.

참고: 참조 타입은 WGSL 모듈에서 작성되지 않습니다. § 6.4.3 참조 및 포인터 타입 참고.

6.1. 타입 검사

WGSL 값은 표현식을 평가하여 계산됩니다. 표현식은 WGSL 문법 규칙 중 이름이 "expression"으로 끝나는 규칙으로 파싱된 소스 텍스트의 일부분입니다. 표현식 E는 외부 표현식 E에 올바르게 포함된 부분 표현식을 포함할 수 있습니다. 최상위 표현식은 스스로 부분 표현식이 아닌 표현식입니다. 자세한 내용은 § 8.18 표현식 문법 요약 참고.

표현식 평가로 생성되는 특정 값은 다음에 따라 결정됩니다:

특정 표현식을 평가한 결과 얻을 수 있는 값들은 항상 특정 WGSL 타입에 속하며, 이를 해당 표현식의 정적 타입이라 합니다. WGSL의 규칙은 표현식의 정적 타입이 오직 해당 표현식의 정적 컨텍스트에만 의존하도록 설계되어 있습니다.

타입 단언은 WGSL 소스 표현식에서 WGSL 타입으로의 매핑입니다. 표기법은 다음과 같습니다:

e : T

이는 e 표현식의 정적 타입이 T임을 의미하는 타입 단언입니다.

참고: 타입 단언은 프로그램 텍스트에 대한 사실 진술이며, 런타임 검사(runtime check)가 아닙니다.

문(statement)은 종종 표현식을 사용하며, 해당 표현식의 정적 타입에 대해 요구 사항을 둘 수 있습니다. 예를 들면:

타입 검사란 성공적으로 파싱된 WGSL 모듈에서 각 표현식을 정적 타입에 매핑하고, 각 문(statement)의 타입 요구 사항이 만족되는지 확인하는 과정입니다. 타입 검사가 실패하면 셰이더 생성 오류의 특별한 경우인 타입 오류가 발생합니다.

타입 검사는 타입 규칙을 구문적 구절(구문적 문장)에 재귀적으로 적용하는 방식으로 수행할 수 있습니다. 여기서 구문적 구절표현식 또는 문(statement)입니다. 타입 규칙구문적 구절정적 컨텍스트가 해당 구절에 포함된 표현식의 정적 타입을 어떻게 결정하는지 설명합니다. 타입 규칙은 두 부분으로 구성됩니다:

타입 규칙은 타입 파라미터를 전제 조건이나 결론에 포함할 수 있습니다. 타입 규칙의 결론이나 전제 조건에 타입 파라미터가 있으면 파라미터화됨이라고 하며, 그렇지 않으면 완전히 구체화됨이라 합니다. 파라미터화된 규칙에서 각 타입 파라미터에 대해 타입을 대입(substitute)하여 완전히 구체화된 타입 규칙을 만들 수 있습니다. 규칙의 타입 파라미터에 타입을 할당하는 작업을 치환(substitution)이라고 합니다.

예를 들어, 논리 부정 (!e 형태의 표현식)에 대한 타입 규칙은 다음과 같습니다:

전제 조건 결론
e: T
T은 bool 또는 vecN<bool>
!e: T

이 규칙은 타입 파라미터 T를 포함하므로 파라미터화된 규칙입니다. Tbool, vec2<bool>, vec3<bool>, vec4<bool> 중 하나의 타입을 나타낼 수 있습니다. Tvec3<bool>로 치환하면 아래와 같은 완전히 구체화된 타입 규칙이 나옵니다:

전제 조건 결론
e: vec3<bool>
!e: vec3<bool>

파라미터화된 규칙에서 치환을 통해 얻을 수 있는 각 완전히 구체화된 규칙을 오버로드라고 합니다. 예를 들어, 불리언 부정 규칙에는 네 가지 오버로드가 있는데, 이는 타입 파라미터 T에 할당할 수 있는 네 가지 타입이 있기 때문입니다.

참고: 즉, 파라미터화된 타입 규칙은 여러 완전히 구체화된 타입 규칙의 패턴을 제공하며, 각각은 파라미터화된 규칙에 다른 치환을 적용해서 얻어집니다.

타입 규칙이 구문적 구절에 적용됨이란 다음 경우를 의미합니다:

파라미터화된 타입 규칙은 어떤 표현식에 대해 치환이 존재하여, 완전히 구체화된 타입 규칙을 만들고, 그 규칙이 해당 표현식에 적용될 때 적용된다고 합니다.

예를 들어, 1u+2u 표현식을 생각해 봅시다. 이 표현식에는 두 개의 리터럴 부분 표현식 1u, 2u가 있고, 둘 다 u32 타입입니다. 최상위 표현식은 덧셈입니다. § 8.7 산술 표현식의 규칙을 참고하면, 덧셈에 대한 타입 규칙이 이 표현식에 적용됩니다. 그 이유는:

구문적 구절을 분석할 때 세 가지 경우가 발생할 수 있습니다:

위 예시에서 1u+2u 표현식에는 하나의 타입 규칙만 적용되므로, 타입 검사는 해당 규칙의 결론인 1u+2u의 타입이 u32임을 받아들입니다.

WGSL 소스 프로그램이 정상적으로 타입이 지정됨(well-typed)이란 다음의 경우입니다:

그렇지 않으면 타입 오류가 발생하며, 해당 소스 프로그램은 올바른 WGSL 모듈이 아닙니다.

WGSL은 정적 타입 언어인데, 이는 WGSL 모듈에 대해 타입 검사를 수행하면 성공하거나 타입 오류를 발견하며, 오직 프로그램 소스 텍스트만 검사하면 되기 때문입니다.

6.1.1. 타입 규칙 표

WGSL 타입 규칙은 표현식에 대해 타입 규칙 표로 구성되어 있으며, 각 타입 규칙마다 한 행이 있습니다.

표현식의 의미론은 해당 표현식을 평가한 효과이며, 주로 결과 값을 생성하는 것입니다. 적용되는 타입 규칙의 설명 열에 해당 표현식의 의미론이 명시되어 있습니다. 의미론은 보통 타입 규칙 파라미터의 값, 그리고 부분 표현식의 가정된 값에 따라 달라집니다. 때로 표현식의 의미론에는 결과 값 생성 외의 효과(부분 표현식의 결과값이 아닌 효과 등)도 포함될 수 있습니다.

예시: 표현식의 부수 효과(side-effect)
fn foo(p : ptr<function, i32>) -> i32 {
  let x = *p;
  *p += 1;
  return x;
}

fn bar() {
  var a: i32;
  let x = foo(&a); // foo 호출은 값을 반환함
                   // 그리고 a의 값을 갱신함
}

6.1.2. 변환 순위

타입 단언 e:T타입 규칙의 전제 조건으로 사용될 때, 다음 중 하나면 만족됩니다:

이 규칙은 아래 표에 정의된 타입 쌍에 대한 ConversionRank 함수로 공식화됩니다. ConversionRank 함수는 한 타입(Src) 값을 다른 타입(Dest)으로 자동 변환하는 우선순위와 가능성을 나타냅니다. 순위가 낮을수록 더 바람직합니다.

가능한 자동 변환Src 타입의 값을 Dest 타입으로 변환하는 것으로, ConversionRank(Src,Dest)가 유한할 때 허용됩니다. 이러한 변환은 § 15.7 부동 소수점 평가에 설명된 제한을 제외하면 값 보존적입니다.

참고: 자동 변환은 두 가지 상황에서만 발생합니다. 첫째, 상수 표현식을 GPU에서 사용할 수 있는 해당 타입의 숫자 값으로 변환할 때. 둘째, 메모리 참조에서 값을 로드할 때 발생합니다.

참고: 변환 순위가 무한대인 변환은 불가능(허용되지 않음)입니다.

참고: 변환이 수행되지 않으면 변환 순위는 0입니다.

한 타입에서 다른 타입으로의 ConversionRank
Src Dest ConversionRank(Src,Dest) 설명
T T 0 동일(Identity). 변환 없음.
ref<AS,T,AM>
주소 공간 AS, 그리고 access mode AMread 또는 read_write일 때.
T 0 로드 규칙을 적용하여 메모리 참조에서 값을 로드.
AbstractFloat f32 1 § 15.7.6 부동 소수점 변환 참고
AbstractFloat f16 2 § 15.7.6 부동 소수점 변환 참고
AbstractInt i32 3 값이 i32에 있으면 동일(Identity). 그렇지 않으면 셰이더 생성 오류 발생.
AbstractInt u32 4 값이 u32에 있으면 동일(Identity). 그렇지 않으면 셰이더 생성 오류 발생.
AbstractInt AbstractFloat 5 § 15.7.6 부동 소수점 변환 참고
AbstractInt f32 6 AbstractInt에서 AbstractFloat로, 그 다음 AbstractFloat에서 f32로 작동
AbstractInt f16 7 AbstractInt에서 AbstractFloat로, 그 다음 AbstractFloat에서 f16으로 작동
vecN<S> vecN<T> ConversionRank(S,T) 구성 요소 타입의 변환 순위 상속.
matCxR<S> matCxR<T> ConversionRank(S,T) 구성 요소 타입의 변환 순위 상속.
array<S,N> array<T,N> ConversionRank(S,T) 구성 요소 타입의 변환 순위 상속. 참고: 고정 크기 배열추상 타입 구성 요소를 가질 수 있음.
__frexp_result_abstract __frexp_result_f32 1
__frexp_result_abstract __frexp_result_f16 2
__frexp_result_vecN_abstract __frexp_result_vecN_f32 1
__frexp_result_vecN_abstract __frexp_result_vecN_f16 2
__modf_result_abstract __modf_result_f32 1
__modf_result_abstract __modf_result_f16 2
__modf_result_vecN_abstract __modf_result_vecN_f32 1
__modf_result_vecN_abstract __modf_result_vecN_f16 2
S T
위의 경우에 해당하지 않음
무한대 그 밖의 타입 간에는 자동 변환이 없음.

타입 T는 타입 S구체화(concretization)이며, 다음이 성립할 때입니다:

e가 타입 T일 때, 그 구체화(concretization)e에 대해 T에서 해당 구체화 타입으로 가능한 변환을 적용한 값을 의미합니다.

참고: f32로의 변환이 항상 f16보다 우선되므로, 자동 변환은 f16을 생성하려면 반드시 모듈에서 확장(extension)이 활성화되어 있어야 합니다.

6.1.3. 오버로드 해결

둘 이상의 타입 규칙이 구문적 구절에 적용될 때는 어떤 규칙을 적용할지 결정하기 위한 동점(타이브레이크) 절차가 사용됩니다. 이 절차를 오버로드 해결이라 하며, 타입 검사가 이미 부분 표현식에 대한 정적 타입을 찾은 상태를 전제로 합니다.

구문적 구절 P와, P에 적용되는 모든 타입 규칙을 생각해 봅시다. 오버로드 해결 알고리즘은 이러한 타입 규칙을 오버로드 후보라 부릅니다. 각 후보에 대해 다음이 성립합니다:

P에 대한 오버로드 해결은 다음과 같이 진행되며, 가장 우선되는 오버로드 후보 하나를 찾는 것이 목표입니다:

  1. 각 후보 C에 대해, 구문적 구절의 부분 표현식에 대한 변환 순위를 열거합니다. 후보의 전제 조건이 만족되었으므로, Pi번째 부분 표현식에 대해:

    • 정적 타입이 계산됨.

    • 해당 표현식의 정적 타입에서 전제 조건의 타입 단언이 요구하는 타입으로 가능한 자동 변환이 존재함. C.R(i)는 그 변환의 ConversionRank입니다.

  2. 부분 표현식 중 하나가 가능한 자동 변환 이후 추상 타입으로 해석되고, 다른 후보의 부분 표현식 중 일부가 상수 표현식이 아닌 경우, 해당 후보를 제외합니다.

    참고: 결과적으로, 구절 내 부분 표현식 중 하나라도 상수 표현식이 아니면, 구절 내 모든 부분 표현식은 구체 타입이어야 합니다.

  3. 후보 순위 결정: 두 오버로드 후보 C1, C2에 대해, C1더 우선됨이란 다음의 조건을 만족할 때입니다:

    • P의 각 표현식 위치 i에서 C1.R(i) ≤ C2.R(i).

      • 즉, C1P에 적용하는 데 필요한 각 표현식 변환이 C2를 적용할 때 요구되는 표현식 변환과 최소한 동일하거나 더 우선됨.

    • 적어도 한 표현식 위치 i에서 C1.R(i) < C2.R(i).

      • 즉, C1을 적용할 때 요구되는 표현식 변환 중 적어도 하나는 C2를 적용할 때보다 엄격히 더 우선됨.

  4. 모든 후보에 대해 더 우선되는 단일 후보 C가 있으면 오버로드 해결이 성공하며, 해당 후보 타입 규칙 C를 반환합니다. 그렇지 않으면 오버로드 해결은 실패합니다.

6.2. 단순 타입

단순 타입이란 불리언 값, 숫자, 벡터, 행렬, 또는 이러한 값들의 집합의 기계적 표현을 위한 타입입니다.

단순 타입스칼라 타입, 원자적 타입, 또는 복합 타입 중 하나입니다.

참고: WGSL의 단순 타입은 C++의 Plain-Old-Data 타입과 유사하지만, 원자적 타입과 추상 수치 타입도 포함합니다.

6.2.1. 추상 수치 타입

이 타입들은 WGSL 소스에서 표기할 수 없습니다. 오직 타입 검사에만 사용됩니다.

특정 표현식은 셰이더 생성 시점에 평가되며, GPU가 직접 구현하는 것보다 더 넓은 숫자 범위와 정밀도를 가질 수 있습니다.

WGSL은 이러한 평가를 위해 두 가지 추상 수치 타입을 정의합니다:

이 타입들로 표현식을 평가할 때 오버플로우, 무한값 또는 NaN 값이 발생하면 안 됩니다.

타입이 추상 수치 타입이거나 추상 수치 타입을 포함하면 추상 타입입니다. 추상 타입이 아니면 구체 타입입니다.

숫자 리터럴에 접미사가 없으면 추상 수치 타입의 값을 나타냅니다:

예시: log2(32) 표현식의 분석 과정:

예시: 1 + 2.5 표현식의 분석 과정:

예시: let x = 1 + 2.5;

예시: 1u + 2.5셰이더 생성 오류가 발생합니다:

예시: -1 * i32(-2147483648)셰이더 생성 오류가 발생하지 않습니다:

예시: 리터럴의 타입 추론
// 명시적 타입의 부호 없는 정수 리터럴
var u32_1 = 1u; // 변수는 u32를 가짐

// 명시적 타입의 부호 있는 정수 리터럴
var i32_1 = 1i; // 변수는 i32를 가짐

// 명시적 타입의 부동 소수점 리터럴
var f32_1 = 1f; // 변수는 f32를 가짐

// 부호 없는 정수 리터럴은 음수로 만들 수 없음
var u32_neg = -1u; // 잘못됨: u32에 대해 단항 마이너스 지원 안 함

// 구체 타입이 요구되지만 어떤 부분도 특정 구체 타입을 강제하지 않으면, 정수 리터럴은 i32 값으로 해석됨:
//   let 선언의 초기화자는 생성 가능 타입(또는 포인터)이어야 함.
//   AbstractInt에서 생성 가능 타입으로의 자동 변환 중 가장 우선순위가 높은(순위 2) 변환은 AbstractInt에서 i32로 변환임.
//   그래서 '1'은 i32로 추론됨.
let some_i32 = 1; // let some_i32: i32 = 1i;와 같음

// 선언 타입에서 추론됨
var i32_from_type : i32 = 1; // 변수는 i32. AbstractInt에서 i32로 변환, 순위 2
var u32_from_type : u32 = 1; // 변수는 u32. AbstractInt에서 u32로 변환, 순위 3

// 접미사 없는 정수 리터럴은 필요할 때 부동 소수점으로 변환 가능:
//   AbstractInt에서 f32로 자동 변환, 순위 5
var f32_promotion : f32 = 1; // 변수는 f32

// 잘못됨: 부동 소수점에서 정수로 변환 불가능
var i32_demotion : i32 = 1.0; // 잘못됨

// 표현식에서 추론됨
var u32_from_expr = 1 + u32_1; // 변수는 u32
var i32_from_expr = 1 + i32_1; // 변수는 i32

// 값은 표현 가능해야 함
let u32_too_large   : u32 = 1234567890123456890; // 잘못됨, 오버플로우
let i32_too_large   : i32 = 1234567890123456890; // 잘못됨, 오버플로우
let u32_large : u32 = 2147483649; // 올바름
let i32_large : i32 = 2147483649; // 잘못됨, 오버플로우
let f32_out_of_range1 = 0x1p500; // 잘못됨, 범위 초과
let f32_hex_lost_bits = 0x1.0000000001p0; // 잘못됨, f32로 정확히 표현 불가

// 최소 정수: AbstractInt에 단항 마이너스, 그 다음 i32로 추론
// AbstractInt에서 생성 가능 타입으로의 자동 변환 중 가장 우선순위가 높은(순위 2) 변환은 AbstractInt에서 i32로 변환임
let i32_min = -2147483648;  // 타입은 i32

// 잘못됨. AbstractInt에서 i32로 변환하지만 값이 범위 초과하여 셰이더 생성 오류 발생
let i32_too_large_2 = 2147483648; // 잘못됨

// 부분 표현식은 AbstractInt와 AbstractFloat로 해석될 수 있음
// 아래 예시는 모두 올바르며 변수 값은 6u임
var u32_expr1 = (1 + (1 + (1 + (1 + 1)))) + 1u;
var u32_expr2 = 1u + (1 + (1 + (1 + (1 + 1))));
var u32_expr3 = (1 + (1 + (1 + (1u + 1)))) + 1;
var u32_expr4 = 1 + (1 + (1 + (1 + (1u + 1))));

// 내장 함수 매개변수에 기반한 추론

// 최우선 후보는 clamp(i32,i32,i32)->i32
let i32_clamp = clamp(1, -5, 5);
// 최우선 후보는 clamp(u32,u32,u32)
// 리터럴은 AbstractInt에서 u32로 자동 변환
let u32_clamp = clamp(5, 0, u32_from_expr);
// 최우선 후보는 clamp(f32,f32,f32)->f32
// 리터럴은 AbstractInt에서 f32로 자동 변환
let f32_clamp = clamp(0, f32_1, 1);

// 아래 예시는 모두 f32로 승격(promote)되어 초기값은 10f임
let f32_promotion1 = 1.0 + 2 + 3 + 4;
let f32_promotion2 = 2 + 1.0 + 3 + 4;
let f32_promotion3 = 1f + ((2 + 3) + 4);
let f32_promotion4 = ((2 + (3 + 1f)) + 4);

// 타입 규칙 위반

// 잘못됨, 초기화자는 f32로만 해석됨:
// AbstractFloat에서 u32로의 가능한 자동 변환 없음
let mismatch : u32 = 1.0;

// 잘못됨, clamp는 부호가 혼합된 매개변수를 허용하는 오버로드가 없음
let ambiguous_clamp = clamp(1u, 0, 1i);

// 구문 수준에서 타입 추론이 완료됨

// let 선언의 초기화자는 생성 가능 타입(또는 포인터)이어야 함
// AbstractInt에서 생성 가능 타입으로의 자동 변환 중 가장 우선순위가 높은(순위 2) 변환은 AbstractInt에서 i32로 변환임
let some_i32 = 1; // let some_i32: i32 = 1i;와 같음

let some_f32 : f32 = some_i32; // 타입 오류: i32는 f32에 할당할 수 없음

// 또 다른 오버플로우 사례
let overflow_u32 = (1 -2) + 1u; // 잘못됨, -1은 u32 범위 초과

// 32비트 범위를 벗어난 이상적 값이지만 다시 범위 내로 복귀
let out_and_in_again = (0x1ffffffff / 8);

// 유사하지만 잘못됨
let out_of_range = (0x1ffffffff / 8u); // 32비트에서 연산해야 하므로 0x1ffffffff는 범위 초과

6.2.2. 불리언 타입

bool 타입은 truefalse 값을 포함합니다.

불리언 리터럴 타입 규칙
전제 조건 결론 설명
true: bool true 값
false: bool false 값

6.2.3. 정수 타입

u32 타입은 32비트 부호 없는 정수 집합입니다.

i32 타입은 32비트 부호 있는 정수 집합입니다. 이진수 보수(two’s complement) 표현을 사용하며, 최상위 비트가 부호 비트입니다.

표현식에서 구체 정수 타입이 오버플로우되는 경우 결과는 2bitwidth로 모듈로(modulo) 처리됩니다.

정수 타입의 극단값
타입 최솟값 최댓값
i32 i32(-2147483648) 2147483647i
i32(-0x80000000) 0x7fffffffi
u32 0u 4294967295u
0x0u 0xffffffffu

참고: AbstractInt도 정수 타입입니다.

6.2.4. 부동 소수점 타입

f32 타입은 IEEE-754 binary32(단정밀도) 형식의 32비트 부동 소수점 값 집합입니다. 자세한 내용은 § 15.7 부동 소수점 평가 참고.

f16 타입은 IEEE-754 binary16(반정밀도) 형식의 16비트 부동 소수점 값 집합입니다. f16 타입을 사용할 때 프로그램에 enable f16; 지시문이 포함되어 f16 확장이 활성화되지 않으면 셰이더 생성 오류가 발생합니다. 자세한 내용은 § 15.7 부동 소수점 평가 참고.

다음 표는 부동 소수점 타입의 일부 극단값을 나열합니다. 각 값에는 대응되는 음수 값이 있습니다.

부동 소수점 타입의 극단값
타입 가장 작은 양의 서브노멀 가장 작은 양의 노멀 가장 큰 양의 유한값 가장 큰 유한 2의 거듭제곱
f32 1.40129846432481707092e-45f 1.17549435082228750797e-38f 3.40282346638528859812e+38f 0x1p+127f
0x1p-149f 0x1p-126f 0x1.fffffep+127f
f16 5.9604644775390625e-8h 0.00006103515625h 65504.0h 0x1p+15h
0x1p-24h 0x1p-14h 0x1.ffcp+15h

참고: AbstractFloat도 부동 소수점 타입입니다.

6.2.5. 스칼라 타입

스칼라 타입은 bool, AbstractInt, AbstractFloat, i32, u32, f32, f16입니다.

수치 스칼라 타입은 AbstractInt, AbstractFloat, i32, u32, f32, f16입니다.

정수 스칼라 타입은 AbstractInt, i32, u32입니다.

스칼라 변환이란 한 스칼라 타입의 값을 다른 스칼라 타입의 값으로 매핑하는 것입니다. 일반적으로 결과 값은 원래 값과 유사하며, 대상 타입의 제한 내에서 결정됩니다. 스칼라 변환은 다음 경우에 발생합니다:

6.2.6. 벡터 타입

벡터란 2, 3, 4개의 스칼라 성분을 그룹화한 시퀀스입니다.

타입 설명
vecN<T> T 타입의 성분 N개로 이루어진 벡터. N은 {2, 3, 4} 중 하나여야 하며, T반드시 스칼라 타입 중 하나여야 합니다. T를 벡터의 성분 타입이라고 합니다.

벡터의 성분 타입이 수치 스칼라인 경우 수치 벡터입니다.

벡터의 주요 용도는 다음과 같습니다:

벡터(및 행렬)에 대한 많은 연산은 성분별로 동작하며, 즉 결과는 각 스칼라 성분에 대해 독립적으로 연산하여 만들어집니다.

예시: 벡터
vec2<f32>  // 두 개의 f32로 이루어진 벡터입니다.
예시: 성분별 덧셈
let x : vec3<f32> = a + b; // a와 b는 vec3<f32>
// x[0] = a[0] + b[0]
// x[1] = a[1] + b[1]
// x[2] = a[2] + b[2]

WGSL은 또한 다음 타입 별칭사전 선언합니다:

사전 선언된 별칭 원래 타입 제한 사항
vec2i vec2<i32>
vec3i vec3<i32>
vec4i vec4<i32>
vec2u vec2<u32>
vec3u vec3<u32>
vec4u vec4<u32>
vec2f vec2<f32>
vec3f vec3<f32>
vec4f vec4<f32>
vec2h vec2<f16> f16 확장이 필요합니다.
vec3h vec3<f16>
vec4h vec4<f16>

6.2.7. 행렬 타입

행렬은 2, 3, 4개의 부동 소수점 벡터를 그룹화한 시퀀스입니다.

타입 설명
matCxR<T> C열, R행, 타입 T의 행렬. C, R은 모두 {2, 3, 4}에 속해야 하며, Tf32, f16, 또는 AbstractFloat이어야 합니다. 동치적으로, C개의 vecR<T> 타입의 열벡터로 볼 수 있습니다.

행렬의 주요 용도는 선형 변환을 구현하는 것입니다. 이 해석에서 행렬의 벡터들은 열벡터로 취급됩니다.

곱셈 연산자(*)는 다음에 사용됩니다:

§ 8.7 산술 표현식 참고.

예시: 행렬
mat2x3<f32>  // 2열 3행의 32비트 부동 소수점 행렬입니다.
             // 동치적으로, vec3<f32> 타입의 열벡터 2개입니다.

WGSL은 또한 다음 타입 별칭사전 선언합니다:

사전 선언된 별칭 원래 타입 제한 사항
mat2x2f mat2x2<f32>
mat2x3f mat2x3<f32>
mat2x4f mat2x4<f32>
mat3x2f mat3x2<f32>
mat3x3f mat3x3<f32>
mat3x4f mat3x4<f32>
mat4x2f mat4x2<f32>
mat4x3f mat4x3<f32>
mat4x4f mat4x4<f32>
mat2x2h mat2x2<f16> f16 확장이 필요합니다.
mat2x3h mat2x3<f16>
mat2x4h mat2x4<f16>
mat3x2h mat3x2<f16>
mat3x3h mat3x3<f16>
mat3x4h mat3x4<f16>
mat4x2h mat4x2<f16>
mat4x3h mat4x3<f16>
mat4x4h mat4x4<f16>

6.2.8. 원자적 타입

원자적 타입구체 정수 스칼라 타입을 캡슐화하며 다음을 만족해야 합니다:

타입 설명
atomic<T> 타입 T의 원자. T반드시 u32 또는 i32여야 합니다.

표현식이 원자적 타입으로 평가되는 것은 허용되지 않습니다.

원자적 타입은 workgroup 주소 공간의 변수 또는 storage buffer 변수 중 read_write 접근 모드를 가진 변수에서만 인스턴스화할 수 있습니다. 해당 타입에 대한 연산의 메모리 범위는 인스턴스화된 주소 공간에 따라 결정됩니다. workgroup 주소 공간의 원자적 타입은 Workgroup 메모리 범위를 가지며, storage 주소 공간의 원자적 타입은 QueueFamily 메모리 범위를 가집니다.

원자적 수정은 원자 객체의 내용을 변경하는 연산입니다. 새 값이 기존 값과 같아도 수정으로 간주됩니다.

WGSL에서 원자적 수정은 각 객체에 대해 상호 순서가 보장됩니다. 즉, 셰이더 단계를 실행하는 동안 각 원자 객체 A에 대해 모든 에이전트는 A에 적용된 수정 연산의 동일한 순서를 관찰합니다. 다른 원자 객체 간의 순서는 관련이 없을 수 있으며, 인과관계가 암시되지 않습니다. workgroup 주소 공간의 변수는 하나의 workgroup 내에서 공유되지만, 서로 다른 workgroup 간에는 공유되지 않습니다.

6.2.9. 배열 타입

배열은 원소 값들의 인덱스 가능한 시퀀스입니다.

타입 설명
array<E,N> 고정 크기 배열, E 타입의 원소가 N개 있음.
N은 배열의 원소 수입니다.
array<E> 런타임 크기 배열, E 타입의 원소로 이루어짐. 이는 특정 맥락에서만 나타날 수 있습니다.

배열의 첫 번째 원소는 인덱스 0에 있고, 이후 각 원소는 다음 정수 인덱스에 위치합니다. § 8.5.3 배열 접근 표현식 참고.

런타임 크기 배열 타입으로 평가되는 표현식은 허용되지 않습니다.

고정 크기 배열의 원소 수 표현식 N은 다음 제약을 만족해야 합니다:

참고: 원소 수 값은 Noverride 선언에 의존하면 파이프라인 생성 시점에, 그렇지 않으면 셰이더 모듈 생성 시점에 완전히 결정됩니다.

참고: 타입 동치성(type-equivalency)을 만족하려면, 상수 표현식이 아닌 override 표현식은 반드시 식별자여야 합니다. Override 가능한 상수로 크기가 지정된 workgroup 변수 예시 참고

런타임 크기 배열의 원소 수는 해당 storage buffer 변수에 연결된 버퍼 바인딩의 크기로 결정됩니다. § 13.3.4 버퍼 바인딩에 의해 결정되는 런타임 크기 배열 원소 수 참고.

배열의 원소 타입은 다음 중 하나여야 합니다:

참고: 원소 타입은 단순 타입이어야 합니다.

두 배열 타입이 동일한 경우는 다음 모두를 만족할 때입니다:

예시: 고정 크기 배열 타입의 예시, 오버라이드 불가 원소 수
// array<f32,8>와 array<i32,8>은 다른 타입입니다:
// 원소 타입이 다름
var<private> a: array<f32,8>;
var<private> b: array<i32,8>;
var<private> c: array<i32,8u>;  // array<i32,8>과 array<i32,8u>는 동일 타입

const width = 8;
const height = 8;

// array<i32,8>, array<i32,8u>, array<i32,width>는 모두 동일 타입입니다.
// 원소 수가 8로 평가됨
var<private> d: array<i32,width>;

// array<i32,height>와 array<i32,width>는 동일 타입입니다.
var<private> e: array<i32,width>;
var<private> f: array<i32,height>;

참고: 오버라이드 가능 상수로 크기가 지정된 배열 타입의 유효한 사용은 workgroup 주소 공간에서 메모리 뷰로 사용하는 경우뿐입니다. 여기에는 workgroup 변수의 스토어 타입이 포함됩니다. § 7 변수와 값 선언 참고.

예시: 오버라이드 가능 상수로 크기가 지정된 workgroup 변수
override blockSize = 16;

var<workgroup> odds: array<i32,blockSize>;
var<workgroup> evens: array<i32,blockSize>; // 동일 타입

// 아래 예시들은 'odds', 'evens'과 동일 타입이 아닙니다.

// 다른 타입: 'blockSize' 식별자가 아님
var<workgroup> evens_0: array<i32,16>;
// 다른 타입: 원소 수를 산술식으로 표현
var<workgroup> evens_1: array<i32,(blockSize * 2 / 2)>;
// 다른 타입: 괄호로 식별자를 감쌈
var<workgroup> evens_2: array<i32,(blockSize)>;

// 잘못된 예시: 오버라이드 가능 원소 수는 반드시 가장 바깥 레벨에만 올 수 있음
// var<workgroup> both: array<array<i32,blockSize>,2>;

// 잘못된 예시: 오버라이드 가능 원소 수는 workgroup 변수에만 유효함
// var<private> bad_address_space: array<i32,blockSize>;

6.2.10. 구조체 타입

구조체는 이름이 있는 멤버 값들의 이름이 있는 그룹입니다.

타입 설명
struct AStructName {
M1 : T1,
...
MN : TN,
}
식별자 AStructName으로 이름이 붙은 구조체 타입 선언이며, N개의 멤버를 가집니다. 각 멤버 i는 식별자 Mi로 이름 붙여지고, 타입 Ti를 가집니다.

N최소 1이어야 합니다.

동일 구조체 타입 내 두 멤버는 이름이 같으면 안 됩니다.

구조체 타입은 모듈 범위에서 선언됩니다. 프로그램 소스의 다른 위치에서는 구조체 타입을 식별자 이름으로 나타냅니다. § 5 선언과 범위 참고.

두 구조체 타입이 동일하려면 이름이 같아야 합니다.

구조체 멤버 타입은 다음 중 하나여야 합니다:

참고: 모든 사용자 선언 구조체 타입은 구체 타입입니다.

참고: 각 멤버 타입은 단순 타입이어야 합니다.

구조체 멤버, 배열 원소 타입의 제한에 따른 결과:

예시: 구조체
// 세 개 멤버를 가진 구조체
struct Data {
  a: i32,
  b: vec2<f32>,
  c: array<i32,10>, // 마지막 컴마는 선택사항
}

// Data 타입 값을 저장하는 변수 선언
var<private> some_data: Data;
struct_decl :

'struct' ident struct_body_decl

struct_body_decl :

'{' struct_member ( ',' struct_member ) * ',' ? '}'

struct_member :

attribute * member_ident ':' type_specifier

구조체 멤버에 적용할 수 있는 속성(attribute)은 다음과 같습니다:

builtin, location, blend_src, interpolate, invariant 속성은 IO 속성입니다. 구조체 S의 멤버에 적용된 IO 속성S형식 매개변수 또는 반환 타입으로서 엔트리 포인트에서 사용될 때에만 효과가 있습니다. § 13.3.1 셰이더 단계 간 입력/출력 인터페이스 참고.

align, size 속성은 레이아웃 속성이며, 구조체 타입이 유니폼 버퍼 또는 스토리지 버퍼 정의에 사용될 때 필요할 수 있습니다. § 14.4 메모리 레이아웃 참고.

예시: 구조체 선언
struct my_struct {
  a: f32,
  b: vec4<f32>
}
예시: 구조체를 이용한 버퍼 선언
// 런타임 배열
alias RTArr = array<vec4<f32>>;
struct S {
  a: f32,
  b: f32,
  data: RTArr
}
@group(0) @binding(0) var<storage> buffer: S;

6.2.11. 복합 타입

타입이 내부 구조를 다른 타입들의 조합(composition)으로 표현한다면 복합입니다. 내부 부분은 겹치지 않으며, 이를 성분이라고 합니다. 복합 값은 성분으로 분해할 수 있습니다. § 8.5 복합 값 분해 표현식 참고.

복합 타입에는 다음이 있습니다:

복합 타입 T에 대해, 중첩 깊이(nesting depth), NestDepth(T)는 다음과 같습니다:

6.2.12. 생성 가능한 타입

많은 종류의 값은 생성, 로드, 저장, 함수에 전달, 함수에서 반환할 수 있습니다. 이를 생성 가능이라고 합니다.

다음 중 하나인 타입은 생성 가능 타입입니다:

참고: 모든 생성 가능 타입은 생성 시점 고정 footprint를 가집니다.

참고: 원자적 타입과 런타임 크기 배열 타입은 생성 가능이 아닙니다. 원자적 또는 런타임 크기 배열을 포함하는 복합 타입도 생성 가능이 아닙니다.

6.2.13. 고정 footprint 타입

변수의 메모리 footprint란 변수의 내용을 저장하는 데 사용되는 메모리 위치의 수입니다. 변수의 메모리 footprint는 스토어 타입에 따라 달라지며, 이는 셰이더 생명주기의 어느 시점에서 확정됩니다. 대부분의 변수는 셰이더 생성 시점에 크기가 결정됩니다. 일부 변수는 파이프라인 생성 시점에, 또 다른 변수는 셰이더 실행 시작까지 늦춰질 수 있습니다.

타입이 생성 시점 고정 footprint를 가진다는 것은, 해당 타입의 구체화의 크기가 셰이더 생성 시점에 완전히 결정된다는 뜻입니다.

타입이 고정 footprint를 가진다는 것은, 크기가 파이프라인 생성 시점에 완전히 결정되는 것을 의미합니다.

참고: 모든 구체 생성 시점 고정 footprint 및 고정 footprint 타입은 저장 가능입니다.

참고: 파이프라인 생성은 셰이더 생성에 의존하므로, 생성 시점 고정 footprint가 있으면 고정 footprint도 가집니다.

생성 시점 고정 footprint를 가진 타입은 다음과 같습니다:

참고: 생성 가능 타입은 생성 시점 고정 footprint를 가집니다.

단순 타입고정 footprint를 가진 것은 다음 중 하나입니다:

참고: 상수 표현식이 아닌 override-expression으로 원소 수가 지정된 고정 크기 배열의 유효한 사용은 workgroup 주소 공간의 메모리 뷰로 사용하는 경우뿐입니다. 여기에는 workgroup 변수의 스토어 타입이 포함됩니다.

참고: 고정 footprint 타입은 직접 또는 간접적으로 원자적 타입을 포함할 수 있지만, 생성 가능 타입은 포함할 수 없습니다.

참고: 고정 footprint 타입에는 런타임 크기 배열과, 런타임 크기 배열을 포함하는 구조체가 제외됩니다.

6.3. 열거 타입

열거 타입은 제한된 개수의 이름이 있는 값 집합입니다. 열거는 특정 개념(예: 유효한 텍셀 포맷 집합 등)의 가능한 선택지를 구분하는 데 사용됩니다.

열거자열거 타입의 이름이 있는 값 중 하나입니다. 각 열거자는 다른 열거자 및 다른 종류의 값들과 구분됩니다.

WGSL 소스에서 새로운 열거자나 열거 타입을 선언하는 방법은 없습니다.

참고: 열거자는 템플릿 파라미터로 사용됩니다.

참고: 열거자 복사나 다른 이름을 생성하는 방법은 없습니다:

6.3.1. 사전 선언 열거자

다음 표는 WGSL의 열거 타입과, 그 사전 선언된 열거자를 나열합니다. 열거 타입은 존재하지만 WGSL 소스에서 표기할 수 없습니다.

사전 선언 열거자
열거
(WGSL에서 표기 불가)
사전 선언 열거자
access mode read
write
read_write
address space

참고: handle 주소 공간은 WGSL 소스에서 표기되지 않습니다.

function
private
workgroup
uniform
storage
texel format rgba8unorm
rgba8snorm
rgba8uint
rgba8sint
rgba16unorm
rgba16snorm
rgba16uint
rgba16sint
rgba16float
rg8unorm
rg8snorm
rg8uint
rg8sint
rg16unorm
rg16snorm
rg16uint
rg16sint
rg16float
r32uint
r32sint
r32float
rg32uint
rg32sint
rg32float
rgba32uint
rgba32sint
rgba32float
bgra8unorm
r8unorm
r8snorm
r8uint
r8sint
r16unorm
r16snorm
r16uint
r16sint
r16float
rgb10a2unorm
rgb10a2uint
rg11b10ufloat

6.4. 메모리 뷰

WGSL 프로그램은 단순 값 계산뿐만 아니라, 메모리에서 값을 읽거나 메모리에 값을 기록하는 경우가 많으며, 이는 메모리 접근 연산을 통해 이루어집니다. 각 메모리 접근은 메모리 뷰를 통해 수행됩니다.

메모리 뷰는 다음으로 구성됩니다:

메모리 뷰의 접근 모드는 해당 주소 공간에서 지원되어야 합니다. § 7 변수와 값 선언 참고.

6.4.1. 저장 가능 타입

변수에 저장된 값은 반드시 저장 가능 타입이어야 합니다. 저장 가능 타입은 WGSL에서 명시적 표현을 가질 수도 있고(§ 14.4.4 값의 내부 레이아웃 참고), 텍스처나 샘플러처럼 불투명할 수도 있습니다.

타입이 저장 가능이려면, 구체 타입이면서 다음 중 하나여야 합니다:

참고: 즉, 저장 가능 타입은 구체 단순 타입, 텍스처 타입, 샘플러 타입입니다.

6.4.2. 호스트 공유 가능 타입

호스트 공유 가능 타입은 호스트와 GPU 사이에서 공유되는 버퍼의 내용을 나타내거나, 형식 변환 없이 호스트와 GPU 사이에서 복사되는 데이터에 사용됩니다. 이 목적에 사용할 때는 레이아웃 속성을 적용할 수 있습니다(§ 14.4 메모리 레이아웃 참고). § 7.3 var 선언에 설명된 대로, 유니폼 버퍼스토리지 버퍼 변수의 스토어 타입반드시 호스트 공유 가능 타입이어야 합니다.

타입이 호스트 공유 가능이려면, 구체 타입이면서 다음 중 하나여야 합니다:

참고: 셰이더 단계 간 입력/출력 타입의 제한은 § 13.3.1 셰이더 단계 간 입력/출력 인터페이스 및 이후 절에 설명되어 있습니다. 해당 타입들도 사이즈가 결정되지만, 계산 방식이 다릅니다.

참고: 텍스처와 샘플러도 호스트와 GPU 사이에서 공유될 수 있지만, 내용은 불투명합니다. 이 절의 호스트 공유 가능 타입은 특히 storageuniform 버퍼에 사용됩니다.

6.4.3. 참조 및 포인터 타입

WGSL에는 메모리 뷰를 표현하는 두 종류의 타입이 있습니다: 참조 타입포인터 타입입니다.

제약 조건 타입 설명
AS주소 공간,
T저장 가능 타입,
AM접근 모드
ref<AS,T,AM> 참조 타입 메모리 뷰 집합과 연관되며, AS 내에서 T 타입 값을 저장하는 메모리 위치, AM 모드의 메모리 접근을 지원합니다.

여기서 T스토어 타입입니다.

참조 타입은 WGSL 소스에서 작성되지 않으며, WGSL 모듈 분석에 사용됩니다.

AS주소 공간,
T저장 가능 타입,
AM접근 모드
ptr<AS,T,AM> 포인터 타입 메모리 뷰 집합과 연관되며, AS 내에서 T 타입 값을 저장하는 메모리 위치, AM 모드의 메모리 접근을 지원합니다.

여기서 T스토어 타입입니다.

포인터 타입은 WGSL 소스에 나타날 수 있습니다.

두 포인터 타입이 동일하려면 주소 공간, 스토어 타입, 접근 모드가 모두 같아야 합니다.

WGSL 모듈을 분석할 때 참조 및 포인터 타입은 주소 공간, 저장 가능 타입, 접근 모드로 완전히 매개변수화됩니다. 이 명세의 코드 예시에서 주석은 이 완전히 매개변수화된 형태를 보여줍니다.

하지만 WGSL 소스 텍스트에서는:

예시: 포인터 타입
fn my_function(
  /* 'ptr<function,i32,read_write>'는 'function' 주소 공간의 메모리 위치를 참조하는 포인터 값의 타입입니다.
     여기에 'i32'가 스토어 타입입니다.
     암시적 접근 모드는 'read_write'입니다.
     기본값은 "주소 공간" 절 참고. */
  ptr_int: ptr<function,i32>,

  // 'ptr<private,array<f32,50>,read_write>'는 'private' 주소 공간의 메모리 위치를 참조하는 포인터 타입입니다.
  // 여기서 스토어 타입은 'array<f32,50>'입니다.
  // 암시적 접근 모드는 'read_write'입니다.
  // 기본값은 "주소 공간" 절 참고.
  ptr_array: ptr<private, array<f32, 50>>
) { }

참조 타입과 포인터 타입은 모두 메모리 뷰 집합입니다: 특정 메모리 뷰는 고유한 참조 값과 고유한 포인터 값에 대응합니다:

ptr<AS,T,AM> 타입의 각 포인터 값 p는 ref<AS,T,AM> 타입의 고유 참조 값 r에 대응하며, 그 반대도 성립합니다. 여기서 pr는 동일한 메모리 뷰를 나타냅니다.

6.4.4. 유효/무효 메모리 참조

참조 값은 유효 또는 무효입니다.

참조는 § 6.4.8 참조와 포인터 값 형성에서 자세히 설명된 대로 형성됩니다. 일반적으로 유효 참조는 다음과 같이 형성됩니다:

일반적으로 무효 메모리 참조는 다음과 같이 형성됩니다:

유효 포인터유효 참조에 대응하는 포인터입니다. 무효 포인터무효 메모리 참조에 대응하는 포인터입니다.

6.4.5. 기원 변수

참조 값 R기원 변수는 다음과 같이 정의됩니다:

포인터 값의 기원 변수는 해당 참조 값의 기원 변수로 정의됩니다.

참고: 기원 변수는 동적 개념입니다. 함수의 형식 매개변수에 대한 기원 변수는 호출 지점에 따라 다릅니다. 서로 다른 호출 지점에서 서로 다른 기원 변수에 대한 포인터를 전달할 수 있습니다.

유효 참조는 항상 어떤 변수의 메모리 위치의 일부 또는 전체에 대해 비어 있지 않은 메모리 뷰에 대응합니다.

참고: 참조가 변수 내부의 메모리 위치에 대응해도 무효일 수 있습니다. 예를 들어, 인덱스가 인덱싱되는 타입에 대해 너무 클 때, 참조된 위치가 다음 형제 데이터 멤버 내부일 수 있습니다.

아래 예시에서 the_particle.position[i] 참조는 i가 0 또는 1일 때만 유효합니다. i가 2일 때는 이 참조는 무효 메모리 참조가 되며, 실제로는 the_particle.color_index의 메모리 위치에 대응합니다.

예시: 변수 내부에 있지만 무효인 메모리 참조
struct Particle {
   position: vec2f,
   velocity: vec2f,
   color_index: i32,
}

@group(0) @binding(0)
var<storage,read_write> the_particle: Particle;

fn particle_velocity_component(p: Particle, i: i32) -> f32 {
  return the_particle.velocity[i]; // i가 0 또는 1일 때 유효 참조
}

6.4.6. 범위 밖 접근

무효 메모리 참조접근하는 연산은 범위 밖 접근입니다.

범위 밖 접근은 프로그램 결함입니다. 실제로 실행된다면 일반적으로:

이러한 이유로 구현체는 절대 해당 접근을 그대로 수행하지 않습니다. 범위 밖 접근을 실행하면 동적 오류가 발생합니다.

참고: 잘못된 스토어 타입 해석의 예시는 이전 섹션의 예시를 참고하세요. i가 2일 때 the_particle.velocity[i]의 타입은 ref<storage,f32,read_write>이며, 즉 f32스토어 타입인 메모리 뷰입니다. 하지만 실제 메모리 위치는 color_index 멤버에 할당되어 있으므로, 저장된 값은 실제로 i32 타입입니다.

참고: 범위 밖 접근은 동적 오류를 유발하며, 다양한 결과가 발생할 수 있습니다.

가능한 결과는 다음을 포함하지만 이에 국한되지 않습니다:

트랩(Trap)

셰이더 호출이 즉시 종료되고 셰이더 단계 출력이 0값으로 설정됨.

무효 로드(Invalid Load)

무효 참조에서 로드는 다음 중 하나를 반환할 수 있음:

무효 저장(Invalid Store)

무효 참조에 저장하면 다음 중 하나가 될 수 있음:

공유 주소 공간의 변수 내부 다른 위치에 무효 로드나 저장이 리다이렉트될 경우 데이터 레이스가 발생할 수 있습니다. 예를 들어, 여러 셰이더 호출이 동시에 실행 중일 때 접근이 배열의 첫 번째 원소로 리다이렉트될 수 있습니다. 만약 하나라도 쓰기 접근이 있고, 동기화되지 않았다면 결과는 데이터 레이스이자 동적 오류입니다.

범위 밖 접근은 균일성 분석의 가정을 무효화합니다. 예를 들어, 어떤 호출이 범위 밖 접근으로 인해 조기에 종료되면, 집합 연산에 더 이상 참여할 수 없습니다. 특히, workgroupBarrier 호출은 셰이더를 멈추게 할 수 있고, 도함수(derivative)는 잘못된 결과를 낼 수 있습니다.

6.4.7. 참조 및 포인터 활용 사례

참조와 포인터는 사용 방식에 따라 구분됩니다:

이렇게 참조를 정의하면 변수의 간단한 관용적 사용이 가능합니다:

예시: 참조 타입은 변수의 간단한 사용을 가능하게 함
@compute @workgroup_size(1)
fn main() {
  // 'i'의 타입은 ref<function,i32,read_write>
  // 'i'의 메모리 위치는 i32 값 0을 저장
  var i: i32 = 0;

  // 'i + 1'은 'i' 부분 표현식이 i32 타입일 때만 타입 규칙에 일치함.
  // 그래서 'i + 1' 표현식은 i32 타입이고, 평가 시점에 'i' 부분 표현식은
  // 그 시점의 'i' 메모리 위치에 저장된 i32 값으로 평가됨.
  let one: i32 = i + 1;

  // 'i'가 참조하는 위치의 값을 2로 갱신
  i = one + 1;

  // 'i'가 참조하는 위치의 값을 5로 갱신
  // 우변의 평가가 할당 전에 발생함
  i = i + 3;
}
예시: 참조 반환은 참조를 통해 로드된 값을 반환함
var<private> age: i32;
fn get_age() -> i32 {
  // return 문 표현식의 타입은 반드시 i32여야 하며, 이는 함수 반환 타입과 일치해야 함.
  // 'age' 표현식의 타입은 ref<private,i32,read_write>임.
  // 로드 규칙 적용: 참조의 스토어 타입이 요구된 표현식 타입과 일치하며, 다른 타입 규칙은 없음.
  // 이 문맥에서 'age'의 평가는 return 실행 시점의 age가 참조하는 메모리 위치에서 로드된 i32 값임.
  return age;
}

fn caller() {
  age = 21;
  // copy_age 상수는 i32 값 21을 가짐
  let copy_age: i32 = get_age();
}

이렇게 포인터를 정의하면 두 가지 주요 활용 사례가 가능합니다:

예시: 변수 일부에 대한 짧은 이름으로 포인터 사용
struct Particle {
  position: vec3<f32>,
  velocity: vec3<f32>
}
struct System {
  active_index: i32,
  timestep: f32,
  particles: array<Particle,100>
}
@group(0) @binding(0)
var<storage,read_write> system: System;

@compute @workgroup_size(1)
fn main() {
  // 스토리지 메모리 내 특정 Particle에 대한 포인터 형성
  let active_particle = &system.particles[system.active_index];

  let delta_position: vec3<f32> = (*active_particle).velocity * system.timestep;
  let current_position: vec3<f32>  = (*active_particle).position;
  (*active_particle).position = delta_position + current_position;
}
예시: 포인터를 형식 매개변수로 사용하는 경우
fn add_one(x: ptr<function,i32>) {
  /* 'x'가 가리키는 위치에 다음 더 큰 정수값을 저장하거나,
     i32의 가장 작은 음수 값으로 래핑합니다.
     좌변에서 단항 '*'는 포인터를 참조로 변환하여,
     대입이 가능하게 합니다. 기본적으로 read_write 접근 모드를 가집니다.
     /* 우변에서는:
        - 단항 '*'는 포인터를 참조로 변환하며, read_write 접근 모드입니다.
        - 덧셈(+)에 대한 유일한 타입 규칙은 '*x'가 i32 타입이어야 하며,
          이는 '*x'의 store type입니다. 그래서 Load Rule이 적용되어
          평가 시점의 '*x' 메모리의 값을 가져옵니다. 즉, 0의 i32 값입니다.
        - 0에 1을 더하여, 우변의 최종 값은 1이 됩니다. */
     1을 '*x'가 가리키는 메모리에 저장합니다. */
  *x = *x + 1;
}

@compute @workgroup_size(1)
fn main() {
  var i: i32 = 0;

  // 'i'의 값을 1로 변경합니다.
  // 단항 '&'를 사용해 'i'의 포인터 값을 가져옵니다.
  // 호출된 함수가 'i'의 메모리에 접근할 수 있고,
  // 값을 변경할 수 있음을 명확히 나타냅니다.
  add_one(&i);
  let one: i32 = i;  // 'one'의 값은 1.
}

6.4.8. 참조와 포인터 값 형성

참조 값은 다음 방법 중 하나로 형성됩니다:

모든 경우에서 결과의 접근 모드는 원래 참조의 접근 모드와 동일합니다.

예시: 복합 참조에서 성분 참조 사용
struct S {
    age: i32,
    weight: f32
}
var<private> person: S;
// 다른 위치에서 'person'은 해당 변수의 메모리에 대한 참조를 나타내며,
// 타입은 ref<private,S,read_write>입니다.

fn f() {
    var uv: vec2<f32>;
    // 이 함수 본문 내에서 'uv'는 해당 변수의 메모리에 대한 참조를 나타내며,
    // 타입은 ref<function,vec2<f32>,read_write>입니다.

    // 대입문의 좌변 평가:
    //   'uv.x'를 평가하여 참조를 얻음:
    //   1. 'uv'를 평가하여 변수 'uv'의 메모리에 대한 참조를 얻음. 결과 타입은 ref<function,vec2<f32>,read_write>임.
    //   2. '.x' 벡터 접근구를 적용하여, 앞 단계에서 얻은 참조가 가리키는 벡터의 첫 번째 성분 메모리에 대한 참조를 얻음.
    //      결과 타입은 ref<function,f32,read_write>임.
    // 대입문의 우변을 평가하면 f32 값 1.0을 얻음.
    // uv.x가 참조하는 저장 메모리에 f32 값 1.0을 저장함.
    uv.x = 1.0;

    // 대입문의 좌변 평가:
    //   'uv[1]'을 평가하여 참조를 얻음:
    //   1. 'uv'를 평가하여 변수 'uv'의 메모리에 대한 참조를 얻음. 결과 타입은 ref<function,vec2<f32>,read_write>임.
    //   2. '[1]' 배열 인덱스 구를 적용하여 앞 단계의 참조가 가리키는 벡터의 두 번째 성분 메모리에 대한 참조를 얻음.
    //      결과 타입은 ref<function,f32,read_write>임.
    // 대입문의 우변을 평가하면 f32 값 2.0을 얻음.
    // uv[1]이 참조하는 저장 메모리에 f32 값 2.0을 저장함.
    uv[1] = 2.0;

    var m: mat3x2<f32>;
    // 'm[2]' 평가 시:
    // 1. 'm'을 평가하여 변수 'm'의 메모리에 대한 참조를 얻음. 결과 타입은 ref<function,mat3x2<f32>,read_write>임.
    // 2. '[2]' 배열 인덱스 구를 적용하여 앞 단계의 참조가 가리키는 행렬의 세 번째 열 벡터 메모리에 대한 참조를 얻음.
    //    결과는 ref<function,vec2<f32>,read_write> 타입임.
    // let 선언은 vec2<f32> 타입이므로, 초기화자는 vec2<f32> 타입이어야 함.
    // Load Rule이 적용되어, 초기화자는 m[2]가 참조하는 메모리에서 해당 시점의 vec2<f32> 값을 읽어옴.
    let p_m_col2: vec2<f32> = m[2];

    var A: array<i32,5>;
    // 'A[4]' 평가 시
    // 1. 'A'를 평가하여 변수 'A'의 메모리에 대한 참조를 얻음. 결과 타입은 ref<function,array<i32,5>,read_write>임.
    // 2. '[4]' 배열 인덱스 구를 적용하여 앞 단계의 참조가 가리키는 배열의 다섯 번째 요소 메모리에 대한 참조를 얻음.
    //    결과는 ref<function,i32,read_write> 타입임.
    // let 선언은 i32 타입이므로, 우변도 i32 타입이어야 함.
    // Load Rule이 적용되어, 초기화자는 A[4]가 참조하는 메모리에서 해당 시점의 i32 값을 읽어옴.
    let A_4_value: i32 = A[4];

    // 'person.weight' 평가 시
    // 1. 'person'을 평가하여 모듈 스코프에 선언된 변수 'person'의 메모리에 대한 참조를 얻음.
    //    결과 타입은 ref<private,S,read_write>임.
    // 2. '.weight' 멤버 접근구를 적용하여 앞 단계의 참조가 가리키는 메모리의 두 번째 멤버 메모리에 대한 참조를 얻음.
    //    결과 타입은 ref<private,f32,read_write>임.
    // let 선언은 f32 타입이므로, 우변도 f32 타입이어야 함.
    // Load Rule이 적용되어, 초기화자는 person.weight가 참조하는 메모리에서 해당 시점의 f32 값을 읽어옴.
    let person_weight: f32 = person.weight;

    // 또는, 동일 문법으로 포인터에서 참조를 생성할 수도 있음.

    let uv_ptr = &uv;
    // 이 함수 본문 내에서 'uv_ptr'은 'uv'의 메모리에 대한 포인터를 나타내며,
    // 타입은 ptr<function,vec2<f32>,read_write>입니다.

    // 대입문의 좌변 평가:
    //   '*uv_ptr'을 평가하여 참조를 얻음:
    //   1. 'uv_ptr'을 평가하여 변수 'uv'의 메모리에 대한 포인터를 얻음. 결과 타입은 ptr<function,vec2<f32>,read_write>임.
    //   2. 간접 참조 연산자를 적용하여 'uv'의 메모리에 대한 참조를 얻음.
    // 대입문의 우변을 평가하면 vec2<f32> 값 (1.0, 2.0)을 얻음.
    // uv가 참조하는 저장 메모리에 (1.0, 2.0) 값을 저장함.
    *uv_ptr = vec2f(1.0, 2.0);

    // 대입문의 좌변 평가:
    //   'uv_ptr.x'를 평가하여 참조를 얻음:
    //   1. 'uv_ptr'을 평가하여 변수 'uv'의 메모리에 대한 포인터를 얻음. 결과 타입은 ptr<function,vec2<f32>,read_write>임.
    //   2. '.x' 벡터 접근구를 적용하여 앞 단계의 참조가 가리키는 벡터의 첫 번째 성분 메모리에 대한 참조를 얻음.
    //      결과 타입은 ref<function,f32,read_write>임.
    // 대입문의 우변을 평가하면 f32 값 1.0을 얻음.
    // uv.x가 참조하는 저장 메모리에 f32 값 1.0을 저장함.
    uv_ptr.x = 1.0;

    // 대입문의 좌변 평가:
    //   'uv_ptr[1]'을 평가하여 참조를 얻음:
    //   1. 'uv_ptr'을 평가하여 변수 'uv'의 메모리에 대한 포인터를 얻음. 결과 타입은 ptr<function,vec2<f32>,read_write>임.
    //   2. '[1]' 배열 인덱스 구를 적용하여 앞 단계의 참조가 가리키는 벡터의 두 번째 성분 메모리에 대한 참조를 얻음.
    //      결과 타입은 ref<function,f32,read_write>임.
    // 대입문의 우변을 평가하면 f32 값 2.0을 얻음.
    // uv[1]이 참조하는 저장 메모리에 f32 값 2.0을 저장함.
    uv_ptr[1] = 2.0;

    let m_ptr = &m;
    // 'm_ptr[2]' 평가 시:
    // 1. 'm_ptr'을 평가하여 변수 'm'의 메모리에 대한 포인터를 얻음. 결과 타입은 ptr<function,mat3x2<f32>,read_write>임.
    // 2. '[2]' 배열 인덱스 구를 적용하여 앞 단계의 참조가 가리키는 행렬의 세 번째 열 벡터 메모리에 대한 참조를 얻음.
    //    결과는 ref<function,vec2<f32>,read_write> 타입임.
    // let 선언은 vec2<f32> 타입이므로, 초기화자는 vec2<f32> 타입이어야 함.
    // Load Rule이 적용되어, 초기화자는 m[2]가 참조하는 메모리에서 해당 시점의 vec2<f32> 값을 읽어옴.
    let p_m_col2: vec2<f32> = m_ptr[2];

    let A_ptr = &A;
    // 'A[4]' 평가 시
    // 1. 'A'를 평가하여 변수 'A'의 메모리에 대한 포인터를 얻음. 결과 타입은 ptr<function,array<i32,5>,read_write>임.
    // 2. '[4]' 배열 인덱스 구를 적용하여 앞 단계의 참조가 가리키는 배열의 다섯 번째 요소 메모리에 대한 참조를 얻음.
    //    결과는 ref<function,i32,read_write> 타입임.
    // let 선언은 i32 타입이므로, 우변도 i32 타입이어야 함.
    // Load Rule이 적용되어, 초기화자는 A[4]가 참조하는 메모리에서 해당 시점의 i32 값을 읽어옴.
    let A_4_value: i32 = A_ptr[4];

    let person_ptr = &person;
    // 'person.weight' 평가 시
    // 1. 'person_ptr'을 평가하여 모듈 스코프에 선언된 변수 'person'의 메모리에 대한 포인터를 얻음.
    //    결과 타입은 ptr<private,S,read_write>임.
    // 2. '.weight' 멤버 접근구를 적용하여 앞 단계의 참조가 가리키는 메모리의 두 번째 멤버 메모리에 대한 참조를 얻음.
    //    결과 타입은 ref<private,f32,read_write>임.
    // let 선언은 f32 타입이므로, 우변도 f32 타입이어야 함.
    // Load Rule이 적용되어, 초기화자는 person.weight가 참조하는 메모리에서 해당 시점의 f32 값을 읽어옴.
    let person_weight: f32 = person_ptr.weight;
}

포인터 값은 다음 방법 중 하나로 형성됩니다:

모든 경우에서 결과의 접근 모드는 원래 포인터의 접근 모드와 동일합니다.

예시: 변수에서 포인터 얻기
// f32 값을 저장하기 위해 private 주소 공간에 변수를 선언합니다.
var<private> x: f32;

fn f() {
    // i32 값을 저장하기 위해 function 주소 공간에 변수를 선언합니다.
    var y: i32;

    // 'x' 이름은 모듈 스코프 변수 'x'로 해석되며,
    // 타입은 ref<private,f32,read_write>입니다.
    // 단항 '&' 연산자를 적용하면 참조가 포인터로 변환됩니다.
    // 접근 모드는 원본 변수의 접근 모드와 동일하므로,
    // 완전히 지정된 타입은 ptr<private,f32,read_write>입니다. 하지만 read_write는
    // function 주소 공간의 기본 접근 모드이므로 이 경우에는 read_write를 명시하지 않아도 됩니다.
    let x_ptr: ptr<private,f32> = &x;

    // 'y' 이름은 함수 스코프 변수 'y'로 해석되며,
    // 타입은 ref<private,i32,read_write>입니다.
    // 단항 '&' 연산자를 적용하면 참조가 포인터로 변환됩니다.
    // 접근 모드는 기본적으로 'read_write'입니다.
    let y_ptr: ptr<function,i32> = &y;

    // 모듈 스코프에서 선언된 변수와 구별되는 새로운 변수입니다.
    var x: u32;

    // 여기서 'x' 이름은 바로 앞에서 선언한 함수 스코프 변수 'x'로 해석되며,
    // 타입은 ref<function,u32,read_write>입니다.
    // 단항 '&' 연산자를 적용하면 참조가 포인터로 변환됩니다.
    // 접근 모드는 기본적으로 'read_write'입니다.
    let inner_x_ptr: ptr<function,u32> = &x;
}

6.4.9. 다른 언어의 참조 및 포인터와의 비교

이 절은 안내용이며, 규범적이지 않습니다.

WGSL의 참조와 포인터는 다른 언어보다 더 제한적입니다. 특히:

참고: 위 규칙에 따라 "댕글링(dangling)" 포인터, 즉 "live" 기원 변수를 참조하지 않는 포인터를 만드는 것은 불가능합니다. 메모리 뷰무효 메모리 참조일 수 있지만, 절대 기원 변수 또는 버퍼와 연결되지 않은 메모리 위치를 접근하지 않습니다.

6.5. 텍스처 및 샘플러 타입

텍셀(texel)텍스처의 가장 독립적으로 접근 가능한 최소 단위 성분인 스칼라 또는 벡터입니다. texeltexture element의 줄임말입니다.

텍스처(texture)는 렌더링에 유용한 특수 연산을 지원하는 텍셀 집합입니다. WGSL에서는 이러한 연산을 텍스처 내장 함수(texture builtin functions)를 통해 수행합니다. 전체 목록은 § 17.7 텍스처 내장 함수 참고.

WGSL의 텍스처는 WebGPU GPUTexture에 해당합니다.

텍스처의 주요 특징은 다음과 같습니다:

텍셀 포맷

각 텍셀의 데이터 표현 방식. § 6.5.1 텍셀 포맷 참고.

차원 수(dimensionality)

좌표 격자의 차원 수 및 좌표 해석 방식. 차원 수는 1, 2, 3 중 하나. 대부분의 텍스처는 직교 좌표계를 사용. 큐브 텍스처는 6개의 정사각형 면을 가지며, 3차원 좌표를 원점에서 큐브를 향하는 방향 벡터로 해석해 샘플링함.

WebGPU GPUTextureViewDimension 참고.

크기(size)

각 차원별 격자 좌표의 범위. mip level에 따라 결정.

mip level 수

mip level 수는 샘플드 텍스처깊이 텍스처에 대해 최소 1이고, 스토리지 텍스처에 대해 1이다.
MIP level 0은 텍스처의 전체 크기 버전을 포함함. 각 다음 mip level은 이전 mip level의 필터링된 버전을 포함하며, 크기는 절반(반올림 포함)임.
텍스처 샘플링 시 명시적 또는 암시적으로 계산된 level-of-detail을 사용해 읽을 mip level을 선택하고, 필터링을 통해 샘플값을 생성함.

배열 여부(arrayed)

텍스처가 배열인지 여부.

  • 비배열(texutre)은 텍셀의 격자(grid).

  • 배열 텍스처는 동일한 격자의 배열임.

배열 크기(array size)

텍스처가 배열일 때 격자 개수.

샘플 개수(sample count)

텍스처가 멀티샘플일 때 샘플 개수.

텍스처의 각 텍셀은 고유한 논리적 텍셀 주소와 연결되어 있습니다. 논리적 텍셀 주소는 다음을 포함하는 정수 튜플입니다:

텍스처의 물리적 조직은 렌더링 연산에 최적화되어 있습니다. 이를 위해 데이터 레이아웃, 데이터 타입, 셰이더 언어에서 직접 표현할 수 없는 내부 연산 등 많은 세부사항이 프로그래머에게 숨겨집니다.

결과적으로 셰이더는 텍스처 변수 내의 텍셀 메모리에 직접 접근할 수 없습니다. 대신 불투명 핸들을 통해 접근합니다:

이러한 방식으로, 텍스처 타입의 지원 연산 집합은 해당 텍스처 타입을 가진 텍스처 내장 함수의 존재 여부에 따라 결정됩니다. 즉, 해당 타입이 형식 매개변수인 내장 함수가 있어야 합니다.

참고: 텍스처 변수에 저장된 핸들은 셰이더에서 변경될 수 없습니다. 즉, 변수는 읽기 전용이며, 실제로 접근하는 하부 텍스처가 변경 가능(예: 쓰기 전용 스토리지 텍스처)일 수 있더라도 그렇습니다.

텍스처 타입은 다음에 정의된 타입 집합입니다:

샘플러텍셀샘플드 텍스처 또는 깊이 텍스처에서 어떻게 접근할지 제어하는 불투명 핸들입니다.

WGSL 샘플러는 WebGPU GPUSampler에 해당합니다.

텍셀 접근은 샘플러의 여러 속성에 의해 제어됩니다:

주소 모드(addressing mode)

텍스처 경계와 범위 밖 좌표 처리 방식 제어. 각 텍스처 차원별로 주소 모드를 독립적으로 설정 가능. WebGPU GPUAddressMode 참고.

필터 모드(filter mode)

최종 결과를 만들기 위해 어떤 텍셀을 접근할지 제어. 필터링은 가장 가까운 텍셀 사용 또는 여러 텍셀 사이 보간 가능. 여러 필터 모드를 독립적으로 설정 가능. WebGPU GPUFilterMode 참고.

LOD 클램프(LOD clamp)

접근하는 최소/최대 디테일 레벨 제어.

비교(comparison)

비교 샘플러에 대한 비교 방식 제어. WebGPU GPUCompareFunction 참고.

최대 이방성(max anisotropy)

샘플러가 사용하는 최대 이방성값 제어.

샘플러는 WGSL 모듈에서 생성할 수 없으며, 상태(위 속성 포함)는 셰이더 내에서 변경 불가하고 WebGPU API에서만 설정할 수 있습니다.

필터링 샘플러(즉, 보간 필터링 사용하는 샘플러)를 필터 불가 포맷의 텍스처와 함께 사용하면 파이프라인 생성 오류입니다.

참고: 샘플러 변수에 저장된 핸들은 셰이더에서 변경할 수 없습니다.

6.5.1. 텍셀 포맷

WGSL에서 특정 텍스처 타입은 텍셀 포맷을 매개변수로 합니다.

텍셀 포맷은 다음으로 특징지어집니다:

채널

각 채널은 스칼라를 담고 있습니다. 텍셀 포맷은 최대 4개의 채널(r, g, b, a)을 가지며, 일반적으로 빨강, 초록, 파랑, 알파에 해당합니다.

채널 포맷

채널의 비트 수와 해당 비트 해석 방식.

WGSL의 각 텍셀 포맷은 동일한 이름의 WebGPU GPUTextureFormat에 대응합니다.

WGSL 소스 코드에서 사용되는 텍셀 포맷은 제한적입니다. 해당 텍셀 포맷을 정의하는 채널 포맷은 아래 채널 포맷 표에 나열되어 있습니다. 마지막에서 두 번째 열은 저장된 채널 비트를 셰이더에서 사용하는 값으로 변환하는 방법을 나타냅니다. 이는 채널 변환 함수(CTF)라고도 합니다. 마지막 열은 셰이더 값을 저장된 채널 비트로 변환하는 방법을 나타냅니다. 이는 역채널 변환 함수(ICTF)라고도 합니다.

참고: 8unorm의 채널 변환 함수는 {0,...,255}를 부동소수점 구간 [0.0, 1.0]으로 매핑합니다.

참고: 8snorm의 채널 변환 함수는 {-128,...,127}을 부동소수점 구간 [-1.0, 1.0]으로 매핑합니다.

채널 포맷
채널 포맷 저장 비트 수 저장 비트 해석 셰이더 타입 셰이더 값(채널 변환 함수) 쓰기 값 T(역채널 변환 함수)
8unorm 8 부호 없는 정수 v ∈ {0,...,255} f32 v ÷ 255 max(0, min(1, T))
8snorm 8 부호 있는 정수 v ∈ {-128,...,127} f32 v ÷ 127 max(-1, min(1, T))
8uint 8 부호 없는 정수 v ∈ {0,...,255} u32 v min(255, T)
8sint 8 부호 있는 정수 v ∈ {-128,...,127} i32 v max(-128, min(127, T))
16unorm 16 부호 없는 정수 v ∈ {0,...,65535} f32 v ÷ 65535 max(0, min(1, T))
16snorm 16 부호 있는 정수 v ∈ {-32768,...,32767} f32 v ÷ 32767 max(-1, min(1, T))
16uint 16 부호 없는 정수 v ∈ {0,...,65535} u32 v min(65535, T)
16sint 16 부호 있는 정수 v ∈ {-32768,...,32767} i32 v max(-32768, min(32767, T))
16float 16 IEEE-754 binary16 16비트 부동소수점 값 v f32 v quantizeToF16(T)
32uint 32 32비트 부호 없는 정수 v u32 v T
32sint 32 32비트 부호 있는 정수 v i32 v T
32float 32 IEEE-754 binary32 32비트 부동소수점 값 v f32 v T
2unorm 2 부호 없는 정수 v ∈ {0,...,3} f32 v ÷ 3 max(0, min(1, T))
2uint 2 부호 없는 정수 v ∈ {0,...,3} u32 v min(3, T)
10unorm 10 부호 없는 정수 v ∈ {0,...,1023} f32 v ÷ 1023 max(0, min(1, T))
10uint 10 부호 없는 정수 v ∈ {0,...,1023} u32 v min(1023, T)
10float 10 10비트 부동소수점 값: 5비트 바이어스 지수, 5비트 분수 v f32 v max(0, T)
11float 11 11비트 부동소수점 값: 5비트 바이어스 지수, 6비트 분수 v f32 v max(0, T)

아래 표에 나오는 텍셀 포맷(스토리지 텍스처용 텍셀 포맷)은 WebGPU plain color formats에 대응하며, WebGPU STORAGE_BINDING 용도로 최소 하나의 접근 모드를 지원합니다. 이러한 텍셀 포맷은 스토리지 텍스처 타입의 매개변수로 사용됩니다(§ 6.5.5 스토리지 텍스처 타입 참고).

텍셀 포맷이 4개의 채널을 모두 갖지 않을 때는:

아래 표의 마지막 열은 채널 포맷 표의 포맷별 채널 변환 함수를 사용합니다.

스토리지 텍스처용 텍셀 포맷
텍셀 포맷 채널 포맷 메모리 순서의 채널들 해당 셰이더 값
rgba8unorm 8unorm r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba8snorm 8snorm r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba8uint 8uint r, g, b, a vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba8sint 8sint r, g, b, a vec4<i32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba16unorm 16unorm r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba16snorm 16snorm r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba16uint 16uint r, g, b, a vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba16sint 16sint r, g, b, a vec4<i32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba16float 16float r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
rg8unorm 8unorm r, g vec4<f32>(CTF(r), CTF(g), 0.0, 1.0)
rg8snorm 8snorm r, g vec4<f32>(CTF(r), CTF(g), 0.0, 1.0)
rg8uint 8uint r, g vec4<u32>(CTF(r), CTF(g), 0u, 1u)
rg8sint 8sint r, g vec4<i32>(CTF(r), CTF(g), 0, 1)
rg16unorm 16unorm r, g vec4<f32>(CTF(r), CTF(g), 0.0, 1.0)
rg16snorm 16snorm r, g vec4<f32>(CTF(r), CTF(g), 0.0, 1.0)
rg16uint 16uint r, g vec4<u32>(CTF(r), CTF(g), 0u, 1u)
rg16sint 16sint r, g vec4<i32>(CTF(r), CTF(g), 0, 1)
rg16float 16float r, g vec4<f32>(CTF(r), CTF(g), 0.0, 1.0)
r32uint 32uint r vec4<u32>(CTF(r), 0u, 0u, 1u)
r32sint 32sint r vec4<i32>(CTF(r), 0, 0, 1)
r32float 32float r vec4<f32>(CTF(r), 0.0, 0.0, 1.0)
rg32uint 32uint r, g vec4<u32>(CTF(r), CTF(g), 0u, 1u)
rg32sint 32sint r, g vec4<i32>(CTF(r), CTF(g), 0, 1)
rg32float 32float r, g vec4<f32>(CTF(r), CTF(g), 0.0, 1.0)
rgba32uint 32uint r, g, b, a vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba32sint 32sint r, g, b, a vec4<i32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgba32float 32float r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
bgra8unorm 8unorm b, g, r, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
r8unorm 8unorm r vec4<f32>(CTF(r), 0.0, 0.0, 1.0)
r8snorm 8snorm r vec4<f32>(CTF(r), 0.0, 0.0, 1.0)
r8uint 8uint r vec4<u32>(CTF(r), 0u, 0u, 1u)
r8sint 8sint r vec4<i32>(CTF(r), 0, 0, 1)
r16unorm 16unorm r vec4<f32>(CTF(r), 0.0, 0.0, 1.0)
r16snorm 16snorm r vec4<f32>(CTF(r), 0.0, 0.0, 1.0)
r16uint 16uint r vec4<u32>(CTF(r), 0u, 0u, 1u)
r16sint 16sint r vec4<i32>(CTF(r), 0, 0, 1)
r16float 16float r vec4<f32>(CTF(r), 0.0, 0.0, 1.0)
rgb10a2unorm r, g, b: 10unorm a: 2unorm r, g, b, a vec4<f32>(CTF(r), CTF(g), CTF(b), CTF(a))
rgb10a2uint r, g, b: 10uint a: 2uint r, g, b, a vec4<u32>(CTF(r), CTF(g), CTF(b), CTF(a))
rg11b10ufloat r, g: 11float b: 10float r, g, b vec4<f32>(CTF(r), CTF(g), CTF(b), 1.0)

WGSL은 위 표의 각 텍셀 포맷에 대해 사전 선언된 열거자를 가집니다.

6.5.2. 샘플드 텍스처 타입

샘플드 텍스처샘플러와 함께 접근할 수 있습니다. 샘플러 없이도 접근이 가능합니다. 샘플드 텍스처는 읽기 접근만 허용합니다.

텍셀 포맷은 텍스처 변수에 바인딩된 format 속성입니다. WebGPU는 텍스처, 바인드 그룹 레이아웃의 sampleType, 그리고 텍스처 변수의 샘플드 타입간의 호환성을 검증합니다.

텍스처는 샘플드 타입으로 매개변수화되며, 반드시 f32, i32, u32 중 하나여야 합니다.

타입 차원 배열 여부
texture_1d<T> 1D 아님
texture_2d<T> 2D 아님
texture_2d_array<T> 2D 맞음
texture_3d<T> 3D 아님
texture_cube<T> Cube 아님
texture_cube_array<T> Cube 맞음

6.5.3. 멀티샘플 텍스처 타입

멀티샘플 텍스처샘플 개수가 1 이상입니다. 이름과 달리 샘플러와 함께 사용할 수 없습니다. 실질적으로 샘플 인덱스를 무시하면 논리적 텍셀 주소당 여러 텍셀 데이터를 저장합니다.

텍셀 포맷은 텍스처 변수에 바인딩된 format 속성입니다. WebGPU는 텍스처, 바인드 그룹 레이아웃의 sampleType, 그리고 텍스처 변수의 샘플드 타입간의 호환성을 검증합니다.

texture_multisampled_2d샘플드 타입으로 매개변수화되며, 반드시 f32, i32, u32 중 하나여야 합니다.

타입 차원 배열 여부
texture_multisampled_2d<T> 2D 아님
texture_depth_multisampled_2d 2D 아님

6.5.4. 외부 샘플드 텍스처 타입

외부 텍스처texture_2d<f32>와 유사한 불투명 2차원 float-샘플드 텍스처 타입이지만, 표현 방식이 다를 수 있습니다. textureLoad 또는 textureSampleBaseClampToEdge 내장 함수를 사용해 읽을 수 있고, 이 함수들은 다양한 표현 방식을 처리합니다.

WebGPU § 6.4 GPUExternalTexture 참고.

타입 차원 배열 여부
texture_external 2D 아님

6.5.5. 스토리지 텍스처 타입

스토리지 텍스처는 샘플러 없이 개별 텍셀 값을 접근할 수 있습니다.

스토리지 텍스처 타입은 스토리지 텍스처용 텍셀 포맷 중 하나로 매개변수화되어야 합니다. 텍셀 포맷은 § 6.5.1 텍셀 포맷에 지정된 변환 함수를 결정합니다.

스토리지 텍스처에 텍셀을 쓸 때는 변환 함수의 역함수를 사용하여 셰이더 값을 저장용 텍셀로 변환합니다.

타입 차원 배열 여부
texture_storage_1d<Format, Access> 1D 아님
texture_storage_2d<Format, Access> 2D 아님
texture_storage_2d_array<Format, Access> 2D 맞음
texture_storage_3d<Format, Access> 3D 아님

6.5.6. 깊이 텍스처 타입

깊이 텍스처sampler_comparison과 함께 접근할 수 있습니다. 샘플러 없이도 접근 가능합니다. 깊이 텍스처는 읽기 접근만 허용합니다.

텍스처의 텍셀 포맷GPUTextureBindingLayout에서 정의됩니다.

타입 차원 배열 여부
texture_depth_2d 2D 아님
texture_depth_2d_array 2D 맞음
texture_depth_cube Cube 아님
texture_depth_cube_array Cube 맞음

6.5.7. 샘플러 타입

샘플러샘플드 텍스처깊이 텍스처에 대한 접근을 중재하며, 다음을 수행합니다:

샘플러 타입에는 다음이 있습니다:

타입 설명
sampler 샘플러. 샘플드 텍스처에 대한 접근을 중재합니다.
sampler_comparison 비교 샘플러. 깊이 텍스처에 대한 접근을 중재합니다.

샘플러는 WebGPU API에서 생성 시 매개변수화됩니다. WGSL 모듈에서 수정할 수 없습니다.

샘플러는 텍스처 내장 함수에서만 사용할 수 있습니다.

sampler
sampler_comparison

6.6. AllTypes 타입

AllTypes 타입은 모든 WGSL 타입의 집합입니다.

WGSL 소스에서 AllTypes 타입을 작성할 방법은 없습니다.

§ 6.9 사전 선언 타입 및 타입 생성자 요약에서 모든 사전 선언 타입과 타입 생성자 목록을 확인할 수 있습니다.

참고: 타입은 일반적인 의미의 값이 아닙니다. 런타임에 셰이더가 조작하는 데이터가 아닙니다.

대신 AllTypes 타입은 타입 검사 규칙이 일반 값을 포함할 수 있는 모든 구문에 적용되도록 존재합니다. WGSL은 타입을 값의 한 종류로 정의하고, 표현식이 타입을 나타내도록 하여 규칙을 일관되게 만듭니다.

동기는 템플릿 파라미터입니다. 다양한 맥락에서 타입, 열거자, 단순 값 등 여러 종류를 나타낼 수 있습니다. 특히, template_arg_expression 문법 규칙은 expression 문법 비단말로 확장됩니다.

6.7. 타입 별칭(Type Alias)

타입 별칭은 기존 타입에 새로운 이름을 선언합니다. 선언은 모듈 범위에서 나타나야 합니다. 범위(scope)는 프로그램 전체입니다.

타입 T가 구조체 타입 S타입 별칭으로 정의된 경우, S 멤버의 모든 속성(애트리뷰트 포함)이 T 멤버에도 적용됩니다.

type_alias_decl :

'alias' ident '=' type_specifier

예시: 타입 별칭
alias Arr = array<i32, 5>;

alias RTArr = array<vec4<f32>>;

alias single = f32;     // f32의 별칭 선언
const pi_approx: single = 3.1415;
fn two_pi() -> single {
  return single(2) * pi_approx;
}

6.8. 타입 지정자 문법(Type Specifier Grammar)

§ 8.17 타입 표현식 참고.

type_specifier :

template_elaborated_ident

template_elaborated_ident :

ident _disambiguate_template template_list ?

참고: 표현식primary_expression 문법 규칙을 거쳐 template_elaborated_ident로 확장되거나, 괄호 표현식을 통해 타입을 나타낼 수 있습니다.

6.9. 사전 선언 타입 및 타입 생성자 요약

WGSL 소스에서 표기할 수 있는 사전 선언 타입은 다음과 같습니다:

WGSL은 또한 frexp, modf, atomicCompareExchangeWeak 내장 함수의 반환 타입을 사전 선언합니다. 하지만 WGSL 소스에서는 표기할 수 없습니다.

사전 선언된 타입 생성자는 다음 표에 나와 있습니다:

사전 선언 타입 생성자
사전 선언 타입 생성자 참조
array § 6.2.9 배열 타입 참고
atomic § 6.2.8 원자적 타입 참고
mat2x2 § 6.2.7 행렬 타입 참고, 행렬 타입의 사전 선언 별칭도 나열됨.

참고: 이 타입들은 값 생성자 표현식에서도 행렬을 생성할 때 사용됩니다.

mat2x3
mat2x4
mat3x2
mat3x3
mat3x4
mat4x2
mat4x3
mat4x4
ptr § 6.4.3 참조 및 포인터 타입 참고
texture_1d § 6.5.2 샘플드 텍스처 타입 참고
texture_2d
texture_2d_array
texture_3d
texture_cube
texture_cube_array
texture_multisampled_2d § 6.5.3 멀티샘플 텍스처 타입 참고
texture_storage_1d § 6.5.5 스토리지 텍스처 타입 참고
texture_storage_2d
texture_storage_2d_array
texture_storage_3d
vec2 § 6.2.6 벡터 타입 참고, 벡터 타입의 사전 선언 별칭도 나열됨.

참고: 이 타입들은 값 생성자 표현식에서도 벡터를 생성할 때 사용됩니다.

vec3
vec4

7. 변수 및 값 선언

변수 선언은 데이터 값에 이름을 부여합니다.

값 선언은 값에 이름을 부여하며, 선언된 이후 그 값은 불변입니다. 값 선언의 네 가지 종류는 const, override, let, 그리고 형식 매개변수 선언이며, 아래에서 더 설명됩니다(§ 7.2 값 선언 참고).

변수 선언은 값을 저장할 메모리 위치에 이름을 부여합니다; 그 곳에 저장된 값은 read_write 접근 모드를 가지면 갱신될 수 있습니다. 변수 선언은 하나(var)이지만, 주소 공간접근 모드에 다양한 옵션을 제공하며, 아래에서 더 설명됩니다(§ 7.3 var 선언 참고).

참고: 값 선언은 연관된 메모리 위치가 없습니다. 예를 들어, 어떤 WGSL 표현식도 그 값에 대한 포인터를 형성할 수 없습니다.

함수 정의 외부에 나타나는 선언은 모듈 범위에 있습니다. 그 이름은 프로그램 전체에서 스코프 내에 있습니다.

함수 정의 내부에 나타나는 선언은 함수 범위에 있습니다. 그 이름은 선언 직후의 문에서부터 선언을 둘러싼 중괄호 리스트의 끝까지 사용 가능합니다. 함수 범위 선언은 동적 컨텍스트입니다.

참고:
변수 및 값 선언은 전반적으로 유사한 문법을 가집니다. 아래 비규범 예시는 변수 및 값 선언의 일반적인 형태를 보여줍니다. [...]는 선택적 부분, ...*는 앞의 항목의 0회 이상 반복, ...+는 앞의 항목의 1회 이상 반복을 의미합니다. 구체적인 문법 규칙은 각 요소의 섹션을 참고하세요.
// 구체적인 값 선언.
             const    name [: type]  = initializer ;
[attribute]* override name [: type] [= initializer];
             let      name [: type]  = initializer ;

// 일반적인 변수 선언 형태.
[attribute]* var[<address_space[, access_mode]>] name [: type] [= initializer];

// 구체적인 변수 선언.
// 함수 범위.
             var[<function>] name [: type] [= initializer];

// 모듈 범위.
             var<private>    name [: type] [= initializer];
             var<workgroup>  name : type;
[attribute]+ var<uniform>    name : type;
[attribute]+ var             name : texture_type;
[attribute]+ var             name : sampler_type;
[attribute]+ var<storage[, access_mode]> name : type;

각 선언은 반드시 명시적으로 타입이나 초기화자를 지정해야 합니다. 타입과 초기화자를 모두 지정할 수 있습니다. 각 선언은 연관된 데이터 값의 타입을 결정하는데, 이를 해당 선언의 실효 값 타입이라고 합니다. 실효 값 타입은 다음과 같습니다:

각 값 또는 변수 선언 종류는, 초기화자 표현식의 형태 및 실효 값 타입에 대해 추가 제약을 둘 수 있습니다.

변수 및 값 선언 기능 요약
선언 변경 가능성 스코프 실효 값 타입1 초기화자 지원 초기화자 표현식2 리소스 인터페이스에 포함
const 불변 모듈 또는 함수 생성 가능 (구체 또는 추상) 필수 상수 표현식 아님
override 불변 모듈 구체 스칼라 선택적3 상수 표현식 또는 override 표현식 아님4
let 불변 함수 구체 생성 가능 또는 포인터 타입 필수 상수 표현식, override 표현식, 또는 런타임 표현식 아님
var<storage, read>
var<storage>
불변 모듈 구체 호스트 공유 가능 불가 맞음.
스토리지 버퍼
var<storage, read_write>5,6 변경 가능 모듈 구체 호스트 공유 가능 불가 맞음.
스토리지 버퍼
var<uniform> 불변 모듈 구체 생성 가능 호스트 공유 가능 불가 맞음.
유니폼 버퍼
var6 불변7 모듈 텍스처 불가 맞음.
텍스처 리소스
var 불변 모듈 샘플러 불가 맞음.
샘플러 리소스
var<workgroup>6,8 변경 가능 모듈 구체 단순 타입고정 footprint9 불가10 아님
var<private> 변경 가능 모듈 구체 생성 가능 선택적10 상수 표현식 또는 override 표현식 아님
var<function>
var
변경 가능 함수 구체 생성 가능 선택적10 상수 표현식, override 표현식, 또는 런타임 표현식 아님
  1. const 선언추상 타입일 수 있으며, 타입이 명시적으로 지정되지 않은 경우에만 해당합니다.

  2. 표현식의 타입은 가능한 자동 변환을 통해 실효 값 타입으로 변환될 수 있어야 합니다.

  3. 초기화자가 지정되지 않은 경우, 파이프라인 생성 시점에 값이 제공되어야 합니다.

  4. Override 선언은 셰이더 인터페이스의 일부이지만, 바인드 리소스는 아닙니다.

  5. 스토리지 버퍼스토리지 텍스처read 이외의 접근 모드를 가진 것은 vertex 셰이더 단계에서 정적으로 접근될 수 없습니다. WebGPU createBindGroupLayout() 참고.

  6. 원자적 타입은 변경 가능한 스토리지 버퍼 또는 workgroup 변수에만 나타날 수 있습니다.

  7. 스토리지 텍스처write 또는 read_write 접근 모드의 데이터는 변경 가능하지만, textureStore 내장 함수를 통해서만 수정할 수 있습니다. 변수 자체는 수정할 수 없습니다.

  8. workgroup 주소 공간의 변수는 compute 셰이더 단계에서만 정적으로 접근될 수 있습니다.

  9. 가장 바깥쪽 배열원소 수override 표현식일 수 있습니다.

  10. 초기화자가 없으면 변수는 기본값으로 초기화됩니다.

7.1. 변수 vs 값

변수 선언은 WGSL 모듈에서 유일하게 변경 가능한 데이터입니다. 값 선언은 항상 불변입니다. 변수는 참조포인터 값의 기반이 될 수 있는데, 변수는 메모리 위치와 연관되어 있기 때문입니다. 반면 값 선언은 포인터나 참조 값의 기반이 될 수 없습니다.

변수 사용은 값 선언 사용보다 일반적으로 비용이 더 높습니다. 변수 사용 시 읽기 또는 쓰기를 위해 해당 변수의 메모리 위치를 추가로 접근해야 하기 때문입니다.

일반적으로 작성자는 다음 순서대로 선언을 사용하는 것이 좋으며, 첫 번째가 가장 추천되는 옵션입니다:

이 순서대로 사용하면 셰이더의 전체 성능이 가장 좋습니다.

7.2. 값 선언

식별자값 선언으로 해석되면, 해당 식별자는 그 값을 나타냅니다.

WGSL은 여러 종류의 값 선언을 제공합니다. 각 선언 종류별 값은 셰이더 생명주기의 서로 다른 시점에 고정됩니다. 각 값 선언 종류와 값이 고정되는 시점은 다음과 같습니다:

참고: 형식 매개변수§ 11 함수에서 설명됩니다.

7.2.1. const 선언

const 선언셰이더 생성 시점에 고정되는 데이터 값에 이름을 지정합니다. 각 const 선언에는 반드시 초기화자가 필요합니다. const 선언은 모듈 또는 함수 범위에서 선언할 수 있습니다. 초기화자 표현식은 상수 표현식이어야 합니다. const 선언의 타입은 구체 또는 추상 생성 가능 타입이어야 합니다. const 선언만 실효 값 타입추상일 수 있습니다.

참고: 추상 숫자 타입은 WGSL에서 표기할 수 없으므로, 타입 추론을 통해서만 사용할 수 있습니다.

예시: 모듈 범위 const 선언
const a = 4;                  // 값이 4인 AbstractInt.
const b : i32 = 4;            // 값이 4인 i32.
const c : u32 = 4;            // 값이 4인 u32.
const d : f32 = 4;            // 값이 4인 f32.
const e = vec3(a, a, a);      // 값이 (4, 4, 4)인 AbstractInt의 vec3.
const f = 2.0;                // 값이 2인 AbstractFloat.
const g = mat2x2(a, f, a, f); // 값이 ((4.0, 2.0), (4.0, 2.0))인 AbstractFloat의 mat2x2.
                              // AbstractInt a는 AbstractFloat으로 변환됨.
                              // AbstractFloat은 AbstractInt로 변환될 수 없음.
const h = array(a, f, a, f);  // 값이 (4.0, 2.0, 4.0, 2.0)인 AbstractFloat의 array 4개 성분.

7.2.2. override 선언

override 선언파이프라인에서 오버라이드 가능한 상수값에 이름을 지정합니다. override 선언은 모듈 범위에서만 선언할 수 있습니다. 파이프라인 오버라이드 가능 상수값은 파이프라인 생성 시점에 결정됩니다. 값은 WebGPU 파이프라인 생성 메서드에서 지정된 값이 있으면 그 값, 아니면 초기화자 표현식의 구체화된 값입니다. override 선언의 실효 값 타입구체 스칼라 타입이어야 합니다.

초기화자 표현식은 선택 사항입니다. 있으면 override 표현식이어야 하며, 파이프라인 오버라이드 가능 상수의 기본값을 나타냅니다. 초기화자가 없으면 파이프라인 생성 시점에 값이 제공되지 않으면 파이프라인 생성 오류가 됩니다.

선언에 id 속성이 적용되면, 리터럴 피연산자는 파이프라인 상수 ID라 하며, 0~65535 사이의 고유 정수여야 합니다. 즉, 두 override 선언이 동일한 파이프라인 상수 ID를 사용할 수 없습니다.

어플리케이션은 파이프라인 생성 시점에 override 선언에 대해 자체 값을 지정할 수 있습니다. 파이프라인 생성 API는 오버라이드 가능한 상수와 상수 타입 값의 매핑을 받습니다. 상수는 파이프라인 오버라이드 가능 상수 식별자 문자열로 식별하며, 이는 지정된 경우 파이프라인 상수 ID의 10진수 문자열이고, 없으면 상수 선언 이름입니다.

예시: 모듈 상수, 파이프라인 오버라이드 가능
@id(0)    override has_point_light: bool = true;  // 알고리즘 제어
@id(1200) override specular_param: f32 = 2.3;     // 수치 제어
@id(1300) override gain: f32;                     // 반드시 오버라이드 필요
          override width: f32 = 0.0;              // API 레벨에서 "width" 이름으로 지정됨
                                                  // 
          override depth: f32;                    // API 레벨에서 "depth" 이름으로 지정됨
                                                  // 반드시 오버라이드 필요
          override height = 2 * depth;            // 기본값은
                                                  // (API에서 지정하지 않으면),
                                                  // 다른 오버라이드 가능 상수에 의존함

7.2.3. let 선언

let 선언은 런타임에 해당 문이 실행될 때마다 고정되는 값에 이름을 지정합니다. let 선언은 함수 범위에서만 선언할 수 있으며, 동적 컨텍스트입니다. let 선언에는 반드시 초기화자 표현식이 필요합니다. 값은 초기화자의 구체화된 값입니다. let 선언의 실효 값 타입구체 생성 가능 타입 또는 포인터 타입이어야 합니다.

예시: 함수 범위 let 선언 상수
// 'blockSize'는 값이 1024인 i32를 나타냅니다.
let blockSize: i32 = 1024;

// 'row_size'는 값이 16u인 u32를 나타냅니다. 타입은 추론됨.
let row_size = 16u;

7.3. var 선언

변수는 특정 저장 가능 타입의 값을 담을 수 있는 메모리에 이름을 부여한 참조입니다.

변수에는 두 가지 타입이 연관됩니다: 스토어 타입(참조된 메모리에 저장될 수 있는 값의 타입)과 참조 타입(변수 자체의 타입)입니다. 만약 변수의 스토어 타입이 T, 주소 공간AS, 접근 모드AM이라면, 참조 타입은 ref<AS,T,AM>입니다. 변수의 스토어 타입은 항상 구체 타입입니다.

변수 선언은 다음을 지정합니다:

식별자가 변수 선언으로 해석되면, 해당 식별자는 변수 메모리에 대한 참조 메모리 뷰를 나타내는 표현식이며, 타입은 변수의 참조 타입입니다. § 8.11 변수 식별자 표현식 참고.

변수 선언의 주소 공간이나 접근 모드가 프로그램 소스에 명시된 경우, var 키워드 뒤에 template list로 표기합니다:

private, storage, uniform, workgroup, handle 주소 공간 변수는 모듈 범위에서만 선언해야 하며, function 주소 공간 변수는 함수 범위에서만 선언해야 합니다. handle과 function을 제외한 모든 주소 공간은 반드시 명시해야 합니다. handle 주소 공간은 명시해서는 안 됩니다. function 주소 공간은 명시 선택적입니다.

접근 모드는 항상 기본값이 있으며, storage 주소 공간 변수를 제외한 나머지 변수에 대해서는 WGSL 소스에서 명시해서는 안 됩니다. § 14.3 주소 공간 참고.

uniform 주소 공간 변수는 유니폼 버퍼 변수입니다. 스토어 타입호스트 공유 가능 생성 가능 타입이어야 하며, 주소 공간 레이아웃 제약을 만족해야 합니다.

storage 주소 공간 변수는 스토리지 버퍼 변수입니다. 스토어 타입호스트 공유 가능 타입이어야 하며, 주소 공간 레이아웃 제약을 만족해야 합니다. 변수는 읽기 또는 읽기/쓰기 접근 모드로 선언할 수 있으며, 기본값은 읽기입니다.

텍스처 리소스실효 값 타입텍스처 타입인 변수입니다. 모듈 범위에서 선언되며, 텍셀 격자에 접근하기 위한 불투명 핸들을 저장합니다. 핸들은 handle 주소 공간에 있으며 항상 읽기 전용입니다. 대부분의 경우 내부 텍셀도 읽기 전용이므로 텍스처 변수는 불변입니다. 쓰기 전용 스토리지 텍스처 또는 읽기/쓰기 스토리지 텍스처는 내부 텍셀이 변경 가능하므로, 관례상 텍스처 변수를 변경 가능이라 합니다.

샘플러 리소스실효 값 타입샘플러 타입인 변수입니다. 모듈 범위에서 선언되며, handle 주소 공간에 존재하고, 불변입니다.

§ 13.3.2 리소스 인터페이스에 설명된 대로, 유니폼 버퍼, 스토리지 버퍼, 텍스처, 샘플러는 셰이더의 리소스 인터페이스를 이룹니다.

변수의 수명이란 셰이더 실행 중, 해당 변수에 메모리 위치가 연관되는 기간입니다. 모듈 범위 변수의 수명은 셰이더 스테이지 전체 실행 기간입니다. privatefunction 주소 공간에는 각 호출별로 독립적인 변수가 존재합니다. 함수 범위 변수는 동적 컨텍스트입니다. 함수 범위 변수의 수명은 스코프에 의해 결정됩니다:

리소스 변수는 중첩된 메모리 위치를 가질 수 있지만, 둘 중 하나라도 변경 가능하면 동적 오류입니다. 수명이 중첩되는 다른 변수들은 절대 중첩된 메모리 위치를 가지지 않습니다. 변수의 수명이 끝나면 해당 메모리는 다른 변수에 사용될 수 있습니다.

참고: WGSL은 변수의 내용이 오직 변수의 수명 동안만 관찰 가능함을 보장합니다.

private, function, workgroup 주소 공간 변수는 생성 시 반드시 초기값을 가집니다. 초기화자가 없으면 초기값은 기본 초기값입니다. 초기값 계산 방식은 아래와 같습니다:

그 외 주소 공간의 변수는 리소스로서 draw 명령 또는 dispatch 명령의 바인딩으로 설정됩니다.

다음 WGSL 코드 스니펫을 참고하세요:

예시: 변수 초기값
var i: i32;         // 초기값은 0. 권장 스타일 아님.
loop {
  var twice: i32 = 2 * i;   // 반복마다 재평가됨.
  i++;
  if i == 5 { break; }
}
루프 본문은 6번 실행됩니다. 변수 i는 0, 1, 2, 3, 4, 5 값을 갖게 되며, 변수 twice는 0, 2, 4, 6, 8 값을 갖게 됩니다.

다음 WGSL 코드 스니펫을 참고하세요:

예시: 변수를 여러 번 읽기
var x: f32 = 1.0;
let y = x * x + x + 1;
x가 변수이므로 모든 접근은 load/store 연산으로 바뀝니다. 하지만 브라우저나 드라이버가 중간 표현을 최적화해서 불필요한 load가 제거될 것으로 기대할 수 있습니다.
예시: 모듈 범위 변수 선언
var<private> decibels: f32;
var<workgroup> worklist: array<i32,10>;

struct Params {
  specular: f32,
  count: i32
}

// 유니폼 버퍼. 항상 읽기 전용이고, 더 엄격한 레이아웃 규칙을 가짐.
@group(0) @binding(2)
var<uniform> param: Params;    // 유니폼 버퍼

// 읽기/쓰기 가능한 스토리지 버퍼
@group(0) @binding(0)
var<storage,read_write> pbuf: array<vec2<f32>>;

// 텍스처와 샘플러는 항상 "handle" 공간에 있음.
@group(0) @binding(1)
var filter_params: sampler;
예시: 버퍼 접근 모드
// 스토리지 버퍼
@group(0) @binding(0)
var<storage,read> buf1: Buffer;       // 읽기만 가능, 쓰기 불가.
@group(0) @binding(0)
var<storage> buf2: Buffer;            // 읽기만 가능, 쓰기 불가.
@group(0) @binding(1)
var<storage,read_write> buf3: Buffer; // 읽기와 쓰기 모두 가능.

struct ParamsTable {weight: f32}

// 유니폼 버퍼. 항상 읽기 전용이며, 더 엄격한 레이아웃 규칙을 가짐.
@group(0) @binding(2)
var<uniform> params: ParamsTable;     // 읽기만 가능, 쓰기 불가.
예시: 함수 범위 변수와 상수
fn f() {
   var<function> count: u32;  // 함수 주소 공간의 변수.
   var delta: i32;            // 또 다른 함수 주소 공간 변수.
   var sum: f32 = 0.0;        // 초기화자 있는 함수 주소 공간 변수.
   var pi = 3.14159;          // 초기화자로 f32 스토어 타입 추론.
}

7.4. 변수 및 값 선언 문법 요약

variable_or_value_statement :

variable_decl

| variable_decl '=' expression

| 'let' optionally_typed_ident '=' expression

| 'const' optionally_typed_ident '=' expression

variable_decl :

'var' _disambiguate_template template_list ? optionally_typed_ident

optionally_typed_ident :

ident ( ':' type_specifier ) ?

global_variable_decl :

attribute * variable_decl ( '=' expression ) ?

global_value_decl :

'const' optionally_typed_ident '=' expression

| attribute * 'override' optionally_typed_ident ( '=' expression ) ?

8. 표현식

표현식은 값이 어떻게 계산되는지 지정합니다.

값 표현식의 종류에 따라 평가 시점과 표현력에 차이가 있습니다. 더 빨리 평가될수록 연산에 제약이 더 많지만, 그 값은 더 많은 위치에서 사용할 수 있습니다. 이러한 트레이드오프는 값 선언별로 다른 유연성으로 이어집니다. 상수 표현식override 표현식은 GPU에서 실행되기 전에 평가되므로, 실제 GPU 코드에서는 해당 표현식의 계산 결과만 필요합니다. 또한 상수 표현식셰이더 생성 시점에 평가되므로, override 표현식보다 더 다양한 상황에서 사용할 수 있습니다. 예를 들어, 함수 범위 변수배열 크기를 지정할 수 있습니다. 런타임 표현식상수 표현식이나 override 표현식이 아닌 표현식입니다. 런타임 표현식은 셰이더 실행 중 GPU에서 계산됩니다. 런타임 표현식은 더 적은 문법 요소에서만 사용될 수 있지만, 더 다양한 표현식(예: 다른 런타임 값)으로 계산될 수 있습니다.

8.1. 조기 평가 표현식

WGSL은 런타임 전에 평가될 수 있는 두 가지 표현식을 정의합니다:

8.1.1. const 표현식

셰이더 생성 시점에 평가될 수 있는 표현식을 상수 표현식이라고 합니다. 어떤 표현식이 상수 표현식이 되려면 모든 식별자가 다음 중 하나로 해석되어야 합니다:

const 표현식의 타입은 반드시 타입 규칙에 따라 생성 시점 고정 footprint 타입으로 해석되어야 합니다.

참고: 추상 타입은 상수 표현식의 추론 타입이 될 수 있습니다.

상수 표현식 E는 다음 경우에만 평가됩니다:

참고: 이 평가 규칙에 따라 단락 연산자 &&||는 오른쪽 하위 표현식의 평가를 보호하며, 하위 표현식의 평가가 정적 타입 결정을 위해 필요하지 않는 한 평가되지 않습니다.

상수 표현식은 WebGPU API 메서드를 구현하는 CPU에 의해 평가될 수 있습니다. 따라서 AbstractFloat 값 연산의 정확도 요구사항은 WebAssembly [WASM-CORE-2]와 ECMAScript [ECMASCRIPT] 등 일반 WebGPU 런타임 환경에서 요구하는 것보다 더 엄격하지 않습니다. 구체 부동소수점 타입(f32 등) 연산의 정확도 요구사항은 § 15.7.4.1 구체 부동소수점 표현식의 정확도에서 명시합니다.

예시: (42)의 분석은 다음과 같습니다:

예시: -5의 분석은 다음과 같습니다:

예시: -2147483648의 분석은 다음과 같습니다:

예시: const minint = -2147483648;의 분석은 다음과 같습니다:

예시: let minint = -2147483648;의 분석은 다음과 같습니다:

예시: false && (10i < i32(5 * 1000 * 1000 * 1000))의 분석은 다음과 같습니다:

예시: false && array<u32, 1 + 2>(0, 1, 2)[0] == 0

8.1.2. override 표현식

파이프라인 생성 시점에 평가될 수 있는 표현식을 override 표현식이라고 합니다. 어떤 표현식이 override 표현식이 되려면 모든 식별자가 다음 중 하나로 해석되어야 합니다:

참고: 모든 상수 표현식은 override 표현식이기도 합니다.

상수 표현식이 아닌 override 표현식은 파이프라인 생성 중에만 검증 또는 평가되며, API로 제공된 값이 override 선언에 대체된 후에만 평가됩니다. 만약 override 선언의 값이 API를 통해 대체되면, 초기화자 표현식(있을 경우)은 평가되지 않습니다. 그렇지 않으면, override 표현식 E는 다음 경우에만 평가됩니다:

참고: 모든 override 표현식이 override 선언의 초기화자로 사용될 수 있는 것은 아닙니다. 이러한 초기화자는 반드시 타입 규칙에 따라 구체 스칼라 타입으로 해석되어야 합니다.

예시: override x = 42;의 분석은 다음과 같습니다:

예시: let y = x + 1;의 분석은 다음과 같습니다:

예시: vec3(x,x,x)의 분석은 다음과 같습니다:

예시: override 표현식으로 인한 셰이더 생성 오류
override a : i32 = 0;
override b = a / 0; // 셰이더 생성 오류,
                    // c를 오버라이드하려고 해도 상관없이 발생함
예시: override 표현식으로 인한 파이프라인 생성 오류
override a : i32 = 0;
override b = 1 / a;

// b는 frag1 셰이더의 일부입니다. frag1을 파이프라인으로 컴파일할 때
// 다음과 같은 경우가 발생할 수 있습니다:
// * b가 오버라이드되면 오류 없음.
// * a가 0이 아닌 값으로 오버라이드되면 오류 없음.
// * a가 0이고 b가 오버라이드되지 않으면 파이프라인 생성 오류.
@fragment
fn frag1() {
  _ = b;
}

// b는 frag2 셰이더의 일부가 아닙니다. frag2를 파이프라인으로 컴파일할 때
// b가 오버라이드되지 않고 a가 0이어도 오류는 발생하지 않습니다.
@fragment
fn frag2() {
}

8.2. 불확정 값

일부 제한적인 경우, 런타임 표현식 평가가 하위 표현식에서 지원되지 않는 값을 사용할 수 있습니다.

이런 경우, 평가 결과는 해당 표현식의 정적 타입불확정 값이 되며, 이는 구현체가 임의로 선택한 정적 타입의 값입니다.

동적 컨텍스트마다 서로 다른 값이 생성될 수 있습니다. 예를 들어, 루프의 각 반복마다 평가가 이루어지면 반복마다 서로 다른 값이 계산될 수 있습니다.

참고: 타입이 부동소수점 타입이고, 구현체가 NaN 값을 지원하면 런타임에 생성된 불확정 값이 NaN일 수 있습니다.

예시: 불확정 값 예시
fn fun() {
   var extracted_values: array<i32,2>;
   const v = vec2<i32>(0,1);

   for (var i: i32 = 0; i < 2 ; i++) {
      // 런타임 표현식으로 벡터를 인덱싱할 때, 인덱스가 벡터의 범위를 벗어나면
      // 벡터 성분 타입의 불확정 값(indeterminate value)을 반환합니다.
      let extract = v[i+5];

      // 이제 'extract'는 i32 타입의 임의 값입니다.

      // 나중에 저장합니다.
      extracted_values[i] = extract;

      if extract == extract {
         // 항상 실행됩니다.
      }
      if extract < 2 {
         // 실행될 수도 있고, 실행되지 않을 수도 있습니다.
         // 원래 벡터 성분은 0과 1이지만,
         // 추출된 값이 꼭 그 값일 필요는 없습니다.
      }
   }
   if extracted_values[0] == extracted_values[1] {
      // 실행될 수도 있고, 실행되지 않을 수도 있습니다.
   }
}

fn float_fun(runtime_index: u32) {
   const v = vec2<f32>(0,1); // 부동소수점 값 벡터

   // 앞의 예와 같이, 'float_extract'도 불확정 값입니다.
   // 부동소수점 타입이므로 NaN일 수 있습니다.
   let float_extract: f32 = v[runtime_index+5];

   if float_extract == float_extract {
      // 이 코드는 반드시 실행되는 것은 아닙니다. 이유:
      //  - 'float_extract'가 NaN일 수 있고,
      //  - NaN은 어떤 다른 부동소수점 값과도 같지 않으며,
      //    다른 NaN과도 같지 않습니다.
   }
}

8.3. 리터럴 값 표현식

스칼라 리터럴 타입 규칙
전제조건 결론 설명
true: bool true 불리언 값
false: bool false 불리언 값
e가 접미사가 없는 정수 리터럴일 때 e: AbstractInt 추상 정수 리터럴 값
e가 접미사가 없는 부동소수점 리터럴일 때 e: AbstractFloat 추상 부동소수점 리터럴 값
ei 접미사가 있는 정수 리터럴일 때 e: i32 32비트 부호 있는 정수 리터럴 값
eu 접미사가 있는 정수 리터럴일 때 e: u32 32비트 부호 없는 정수 리터럴 값
ef 접미사가 있는 부동소수점 리터럴일 때 e: f32 32비트 부동소수점 리터럴 값
eh 접미사가 있는 부동소수점 리터럴일 때 e: f16 16비트 부동소수점 리터럴 값

8.4. 괄호 표현식

괄호 표현식 타입 규칙
전제조건 결론 설명
e : T ( e ) : T e로 평가됨.
괄호를 사용해 표현식을 주변 텍스트에서 분리함.

8.5. 복합 값 분해 표현식

이 절에서는 복합 값의 성분을 얻는 표현식과, 복합 값을 담고 있는 메모리 뷰에서 성분의 참조를 얻는 방법을 설명합니다. 여기서 복합 값 또는 복합 값에 대한 메모리 뷰를 base라고 합니다.

방법은 두 가지가 있습니다:

이름 성분 표현식

base B에 대해, B 뒤에 마침표 '.' (U+002D), 그리고 성분 이름을 적습니다.

  • 이 방법은 B벡터 타입이나 구조체 타입, 또는 벡터/구조체 타입에 대한 메모리 뷰일 때 지원됩니다.

  • 유효한 이름은 B의 타입에 따라 달라집니다.

인덱싱 표현식

base 표현식 뒤에 '[' (U+005B), 인덱스 표현식, 그리고 ']' (U+005D)를 붙입니다.

문법적으로, 이 두 형식은 component_or_swizzle_specifier 문법 규칙으로 구현됩니다.

인덱싱 표현식의 인덱스 값 i범위 내 인덱스이려면 0 ≤ i < N을 만족해야 하며, N은 복합 타입의 성분(요소) 개수입니다:

인덱스 값이 범위 내 인덱스가 아니면 범위 외 인덱스입니다. 범위 외 인덱스는 흔히 프로그래밍 오류이며, 오류를 일으킵니다. 자세한 내용은 아래에서 설명합니다.

또한 벡터 타입은 다른 벡터의 성분으로 새 벡터 값을 만드는 swizzling 문법을 지원합니다.

8.5.1. 벡터 성분 접근 표현식

벡터의 성분 접근은 다음 방식으로 할 수 있습니다:

편의 이름은 . 표기법으로 접근합니다(color.bgra 등).

편의 문자는 섞어 쓸 수 없습니다. 예를 들어 .rybw는 사용할 수 없습니다.

편의 문자는 벡터의 끝을 넘어 성분을 접근할 수 없습니다.

편의 문자는 원하는 만큼 중복해서, 아무 순서로 쓸 수 있습니다. 제공하는 문자의 개수는 반드시 1~4개 사이여야 합니다. 즉, 편의 문자를 사용하면 스칼라 타입 또는 올바른 벡터 타입만 만들 수 있습니다.

결과 타입은 제공한 문자의 개수에 따라 달라집니다. vec4<f32>를 예시로 들면:

접근자 결과 타입
r f32
rg vec2<f32>
rgb vec3<f32>
rgba vec4<f32>
var a: vec3<f32> = vec3<f32>(1., 2., 3.);
var b: f32 = a.y;          // b = 2.0
var c: vec2<f32> = a.bb;   // c = (3.0, 3.0)
var d: vec3<f32> = a.zyx;  // d = (3.0, 2.0, 1.0)
var e: f32 = a[1];         // e = 2.0
8.5.1.1. 벡터 단일 성분 선택
벡터 분해: 단일 성분 선택
전제조건 결론 설명
e: vecN<T>
e.x: T
e.r: T
e의 첫 번째 성분 선택

단일 문자 스위즐입니다.

e: vecN<T>
e.y: T
e.g: T
e의 두 번째 성분 선택

단일 문자 스위즐입니다.

e: vecN<T>
N is 3 or 4
e.z: T
e.b: T
e의 세 번째 성분 선택

단일 문자 스위즐입니다.

e: vec4<T> e.w: T
e.a: T
e의 네 번째 성분 선택

단일 문자 스위즐입니다.

e: vecN<T>
i: i32 or u32
T is 구체 타입
e[i]: T 벡터의 i번째 성분 선택
첫 번째 성분은 i=0.

i가 [0,N-1] 범위를 벗어나면:

e: vecN<T>
i: i32 or u32
T is 추상 타입
i상수 표현식일 때
e[i]: T 벡터의 i번째 성분 선택
첫 번째 성분은 i=0.

i가 [0,N-1] 범위를 벗어나면 셰이더 생성 오류입니다.

참고: 추상 벡터 값 e가 상수 표현식이 아닌 표현식으로 인덱싱될 경우, 벡터는 인덱스 적용 전에 구체화됩니다.

8.5.1.2. 벡터 다중 성분 선택

이 절의 표현식은 모두 다중 문자 스위즐입니다. 각각은 다른 벡터의 성분으로부터 벡터를 만듭니다.

다중 문자 스위즐대입문의 좌변에 올 수 없습니다: 대입문의 좌변은 참조 타입이어야 하는데, 다중 문자 스위즐 표현식은 항상 벡터 타입 값을 반환합니다.

벡터 분해: 다중 성분 선택
전제조건 결론 설명
e: vecN<T> 또는 ptr<AS,vecN<T,AM>>
Ix, y, z, w 중 하나
Jx, y, z, w 중 하나
AMread 또는 read_write
e.IJ: vec2<T>
첫 번째 성분 e.I, 두 번째 성분 e.J로 2성분 벡터 생성
zN이 3 또는 4일 때만 유효
wN이 4일 때만 유효

e가 포인터면, 먼저 간접 참조를 적용하고 로드 규칙을 호출함.
e: vecN<T> 또는 ptr<AS,vecN<T,AM>>
Ir, g, b, a 중 하나
Jr, g, b, a 중 하나
AMread 또는 read_write
e.IJ: vec2<T>
첫 번째 성분 e.I, 두 번째 성분 e.J로 2성분 벡터 생성
bN이 3 또는 4일 때만 유효
aN이 4일 때만 유효

e가 포인터면, 먼저 간접 참조를 적용하고 로드 규칙을 호출함.
e: vecN<T> 또는 ptr<AS,vecN<T,AM>>
I,J,Kx, y, z, w 중 하나
AMread 또는 read_write
e.IJK: vec3<T>
첫 번째 성분 e.I, 두 번째 e.J, 세 번째 e.K로 3성분 벡터 생성
zN이 3 또는 4일 때만 유효
wN이 4일 때만 유효

e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함.
e: vecN<T> 또는 ptr<AS,vecN<T,AM>>
I,J,Kr, g, b, a 중 하나
AMread 또는 read_write
e.IJK: vec3<T>
첫 번째 성분 e.I, 두 번째 e.J, 세 번째 e.K로 3성분 벡터 생성
bN이 3 또는 4일 때만 유효
aN이 4일 때만 유효

e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함.
e: vecN<T> 또는 ptr<AS,vecN<T,AM>>
I,J,K,Lx, y, z, w 중 하나
AMread 또는 read_write
e.IJKL: vec4<T>
첫 번째 e.I, 두 번째 e.J, 세 번째 e.K, 네 번째 e.L로 4성분 벡터 생성
zN이 3 또는 4일 때만 유효
wN이 4일 때만 유효

e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함.
e: vecN<T> 또는 ptr<AS,vecN<T,AM>>
I,J,K,Lr, g, b, a 중 하나
AMread 또는 read_write
e.IJKL: vec4<T>
첫 번째 e.I, 두 번째 e.J, 세 번째 e.K, 네 번째 e.L로 4성분 벡터 생성
bN이 3 또는 4일 때만 유효
aN이 4일 때만 유효

e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함.

참고: 위 표에서 참조 타입로드 규칙을 통해 암묵적으로 처리됩니다.

8.5.1.3. 벡터 메모리 뷰에서 성분 참조

이 절의 표현식은 전체 벡터의 메모리 뷰에서 벡터의 단일 성분에 대한 메모리 뷰를 생성합니다.

WGSL 타입 규칙에 따라 이런 표현식은 다음 위치에 올 수 있습니다:

벡터 성분에 대한 쓰기 접근은 해당 벡터의 메모리 위치 전체를 접근할 수 있습니다.

참고: 즉, 서로 다른 호출에서 메모리의 벡터 성분에 접근할 때, 하나라도 쓰기 접근이면 동기화가 필요합니다. § 17.11 동기화 내장 함수 참고.

벡터 메모리 뷰에서 성분 참조 얻기
전제조건 결론 설명
r: ref<AS,vecN<T>,AM> 또는
ptr<AS,vecN<T>,AM>
r.x: ref<AS,T,AM>
r.r: ref<AS,T,AM>
메모리 뷰 r가 참조하는 벡터의 첫 번째 성분에 대한 참조 계산
결과 참조의 원본 변수r의 원본 변수와 동일함.
r: ref<AS,vecN<T>,AM> 또는
ptr<AS,vecN<T>,AM>
r.y: ref<AS,T,AM>
r.g: ref<AS,T,AM>
메모리 뷰 r가 참조하는 벡터의 두 번째 성분에 대한 참조 계산
결과 참조의 원본 변수r의 원본 변수와 동일함.
r: ref<AS,vecN<T>,AM> 또는
ptr<AS,vecN<T>,AM>
N이 3 또는 4
r.z: ref<AS,T,AM>
r.b: ref<AS,T,AM>
메모리 뷰 r가 참조하는 벡터의 세 번째 성분에 대한 참조 계산
결과 참조의 원본 변수r의 원본 변수와 동일함.
r: ref<AS,vec4<T>,AM> 또는
ptr<AS,vec4<T>,AM>
r.w: ref<AS,T,AM>
r.a: ref<AS,T,AM>
메모리 뷰 r가 참조하는 벡터의 네 번째 성분에 대한 참조 계산
결과 참조의 원본 변수r의 원본 변수와 동일함.
r: ref<AS,vecN<T>,AM> 또는
ptr<AS,vecN<T>,AM>
i: i32 또는 u32
r[i] : ref<AS,T,AM>
메모리 뷰 r가 참조하는 벡터의 i번째 성분에 대한 참조 계산

i가 [0,N-1] 범위를 벗어나면:

결과 참조의 원본 변수r의 원본 변수와 동일함.

8.5.2. 행렬 접근 표현식

열 벡터 추출
전제조건 결론 설명
e: matCxR<T>
i: i32 또는 u32
T구체 타입
e[i]: vecR<T> ei번째 열 벡터가 결과값임

i가 [0,C-1] 범위를 벗어나면:

e: matCxR<T>
i: i32 또는 u32
T추상 타입
i상수 표현식일 때
e[i]: vecR<T> ei번째 열 벡터가 결과값임

i가 [0,C-1] 범위를 벗어나면 셰이더 생성 오류입니다.

참고: 추상 행렬 값 e가 상수 표현식이 아닌 표현식으로 인덱싱될 경우, 행렬은 인덱스 적용 전에 구체화됩니다.

행렬 메모리 뷰에서 열 벡터 참조 얻기
전제조건 결론 설명
r: ref<AS,matCxR<T>,AM> 또는
ptr<AS,matCxR<T>,AM>
i: i32 또는 u32
r[i] : ref<AS,vecR<T>,AM> 메모리 뷰 r가 참조하는 행렬의 i번째 열 벡터에 대한 참조 계산

i가 [0,C-1] 범위를 벗어나면:

결과 참조의 원본 변수r의 원본 변수와 동일합니다.

8.5.3. 배열 접근 표현식

배열 요소 추출
전제조건 결론 설명
e: array<T,N>
i: i32 또는 u32
T구체 타입
e[i] : T 결과는 배열 값 ei번째 요소 값임.

i가 [0,N-1] 범위를 벗어나면:

e: array<T,N>
i: i32 또는 u32
T추상 타입
i상수 표현식일 때
e[i] : T 결과는 배열 값 ei번째 요소 값임.

i가 [0,N-1] 범위를 벗어나면 셰이더 생성 오류입니다.

참고: 추상 배열 값 e가 상수 표현식이 아닌 표현식으로 인덱싱될 경우, 배열은 인덱스 적용 전에 구체화됩니다.

배열 메모리 뷰에서 배열 요소 참조 얻기
전제조건 결론 설명
r: ref<AS,array<T,N>,AM> 또는
ptr<AS,array<T,N>,AM>
i: i32 또는 u32
r[i] : ref<AS,T,AM> 메모리 뷰 r가 참조하는 배열의 i번째 요소에 대한 참조 계산

i가 [0,N-1] 범위를 벗어나면:

결과 참조의 원본 변수r의 원본 변수와 동일합니다.

r: ref<AS,array<T>,AM> 또는
ptr<AS,array<T>,AM>
i: i32 또는 u32
r[i] : ref<AS,T,AM> 메모리 뷰 r가 참조하는 런타임 크기 배열의 i번째 요소에 대한 참조 계산

런타임에 배열이 N개의 요소를 가지고 있고 i가 [0,N-1] 범위를 벗어나면, 표현식은 잘못된 메모리 참조로 평가됩니다.

i가 부호 있는 정수이고 i가 0보다 작으면:

결과 참조의 원본 변수r의 원본 변수와 동일합니다.

8.5.4. 구조체 접근 표현식

구조체 멤버 추출
전제조건 결론 설명
S는 구조체 타입
MS의 멤버 식별자 이름이고 타입은 T
e: S
e.M: T 결과는 구조체 값 e에서 이름이 M인 멤버의 값임.
구조체 메모리 뷰에서 구조체 멤버 참조 얻기
전제조건 결론 설명
S는 구조체 타입
MS의 멤버 식별자 이름이고 타입은 T
r: ref<AS,S,AM> 또는
ptr<AS,S,AM>
r.M: ref<AS,T,AM> 구조체에 대한 메모리 뷰를 주면, 결과는 식별자 이름 M인 구조체 멤버에 대한 참조임.
결과 참조의 원본 변수r의 원본 변수와 동일함.

8.6. 논리 표현식

단항 논리 연산
전제조건 결론 설명
e: T
T는 bool 또는 vecN<bool>
!e: T 논리 부정. efalse면 결과는 true, etrue면 결과는 false임. 성분별 처리됨. T가 벡터일 때.
이항 논리 표현식
전제조건 결론 설명
e1: bool
e2: bool
e1 || e2: bool 단락 평가 "or". e1 또는 e2 중 하나라도 true면 true; e1이 false일 때만 e2 평가.
e1: bool
e2: bool
e1 && e2: bool 단락 평가 "and". e1e2가 모두 true면 true; e1이 true일 때만 e2 평가.
e1: T
e2: T
T는 bool 또는 vecN<bool>
e1 | e2: T 논리 "or". 성분별 처리됨. T가 벡터일 때. e1e2 모두 평가.
e1: T
e2: T
T는 bool 또는 vecN<bool>
e1 & e2: T 논리 "and". 성분별 처리됨. T가 벡터일 때. e1e2 모두 평가.

8.7. 산술 표현식

단항 산술 표현식
전제조건 결론 설명
e: T
T가 AbstractInt, AbstractFloat, i32, f32, f16, vecN<AbstractInt>, vecN<AbstractFloat>, vecN<i32>, vecN<f32>, 또는 vecN<f16>
-e: T 부호 반전. 성분별 처리, T가 벡터일 때. T정수 스칼라 타입이고 e가 가장 큰 음수 값이라면 결과는 e 그대로임.
이항 산술 표현식
전제조건 결론 설명
e1 : T
e2 : T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
e1 + e2 : T 덧셈. 성분별 처리, T가 벡터일 때.

T가 부동소수점 타입일 때, 스칼라 정의역은 모든 확장 실수 (x,y) 쌍 중 다음을 제외한 집합임:

  • (−∞,+∞)

  • (+∞,−∞)

e1 : T
e2 : T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
e1 - e2 : T 뺄셈. 성분별 처리, T가 벡터일 때.

T가 부동소수점 타입일 때, 스칼라 정의역은 모든 확장 실수 (x,y) 쌍 중 다음을 제외한 집합임:

  • (−∞,−∞)

  • (+∞,+∞)

e1 : T
e2 : T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
e1 * e2 : T 곱셈. 성분별 처리, T가 벡터일 때.

T가 부동소수점 타입일 때, 스칼라 정의역은 모든 확장 실수 (x,y) 쌍 중 다음을 제외한 집합임:

  • (0,−∞)

  • (0,+∞)

  • (−∞, 0)

  • (+∞, 0)

e1 : T
e2 : T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
e1 / e2 : T 나눗셈. 성분별 처리, T가 벡터일 때.

T가 부호 있는 정수 스칼라 타입일 때 평가 방식:

참고: truncation 동작을 보장하려면 구현체가 unsigned division보다 더 많은 연산을 해야 할 수 있습니다. 두 피연산자가 같은 부호임이 분명하다면 unsigned division을 사용하세요.

T가 부호 없는 정수 스칼라 타입일 때 평가 방식:

T가 부동소수점 타입일 때, 스칼라 정의역은 모든 확장 실수 (x,y) 쌍 중 다음을 제외한 집합임:

  • (0,0)

  • (−∞,−∞)

  • (−∞,+∞)

  • (+∞,−∞)

  • (+∞,+∞)

e1 : T
e2 : T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
e1 % e2 : T 나머지. 성분별 처리, T가 벡터일 때.

T가 부호 있는 정수 스칼라 타입일 때, e1, e2를 한 번씩 평가하고 결과는:

참고: 0이 아니면 결과는 e1과 같은 부호를 가짐.

참고: 일관된 동작을 보장하려면 구현체가 unsigned remainder보다 더 많은 연산을 해야 할 수 있음.

T가 부호 없는 정수 스칼라 타입일 때 평가 방식:

T가 부동소수점 타입일 때, 결과는
e1 - e2 * trunc(e1 / e2) 임.

T가 부동소수점 타입일 때, 스칼라 정의역은 모든 확장 실수 (x,y) 쌍 중 다음을 제외한 집합임:

  • x / y의 정의역을 벗어난 경우:

    • (0,0)

    • (−∞,−∞)

    • (−∞,+∞)

    • (+∞,−∞)

    • (+∞,+∞)

  • y * trunc(x / y)의 정의역을 추가로 벗어난 경우:

    • y가 무한대이고 x가 유한이면, trunc(x / y)는 0임.

    • y가 0이고 x가 무한이면, trunc(x / y)는 무한대임.

스칼라와 벡터 피연산자 혼합의 이항 산술 표현식
전제조건 결론 의미
S는 AbstractInt, AbstractFloat, f32, f16, i32, u32 중 하나
V는 vecN<S>
es: S
ev: V
ev + es: V ev + V(es)
es + ev: V V(es) + ev
ev - es: V ev - V(es)
es - ev: V V(es) - ev
ev * es: V ev * V(es)
es * ev: V V(es) * ev
ev / es: V ev / V(es)
es / ev: V V(es) / ev
ev % es: V ev % V(es)
es % ev: V V(es) % ev
행렬 산술
전제조건 결론 의미
e1, e2: matCxR<T>
T는 AbstractFloat, f32, 또는 f16
e1 + e2: matCxR<T>
행렬 덧셈: 결과는 성분별로 계산되며, 결과의 열 ie1[i] + e2[i]
e1 - e2: matCxR<T> 행렬 뺄셈: 결과는 성분별로 계산되며, 결과의 열 ie1[i] - e2[i]
m: matCxR<T>
s: T
T는 AbstractFloat, f32, 또는 f16
m * s: matCxR<T>
성분별 스케일링: (m * s)[i][j]는 m[i][j] * s
s * m: matCxR<T>
성분별 스케일링: (s * m)[i][j]는 m[i][j] * s
m: matCxR<T>
v: vecC<T>
T는 AbstractFloat, f32, 또는 f16
m * v: vecR<T>
선형대수 행렬-열벡터 곱: 결과의 성분 idot(transpose(m)[i],v)
m: matCxR<T>
v: vecR<T>
T는 AbstractFloat, f32, 또는 f16
v * m: vecC<T>
선형대수 행벡터-행렬 곱:
transpose(transpose(m) * transpose(v))
e1: matKxR<T>
e2: matCxK<T>
T는 AbstractFloat, f32, 또는 f16
e1 * e2: matCxR<T>
선형대수 행렬 곱셈.

8.8. 비교 표현식

비교
전제조건 결론 설명
e1: T
e2: T
S는 AbstractInt, AbstractFloat, bool, i32, u32, f32, 또는 f16
TS 또는 vecN<S>
TBT가 벡터일 때 vecN<bool>이고,
아니면 bool
e1 == e2: TB 동등 비교. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S는 AbstractInt, AbstractFloat, bool, i32, u32, f32, 또는 f16
TS 또는 vecN<S>
TBT가 벡터일 때 vecN<bool>이고,
아니면 bool
e1 != e2: TB 비동등 비교. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
TBT가 벡터일 때 vecN<bool>이고,
아니면 bool
e1 < e2: TB 미만 비교. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
TBT가 벡터일 때 vecN<bool>이고,
아니면 bool
e1 <= e2: TB 이하 비교. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
TBT가 벡터일 때 vecN<bool>이고,
아니면 bool
e1 > e2: TB 초과 비교. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T가 S, 또는 vecN<S>
TBT가 벡터일 때 vecN<bool>이고,
아니면 bool
e1 >= e2: TB 이상 비교. 성분별 처리, T가 벡터일 때.

8.9. 비트 표현식

단항 비트 연산
전제조건 결론 설명
e: T
S는 AbstractInt, i32, 또는 u32
T는 S 또는 vecN<S>
~e : T e에 대한 비트 반전 연산. 결과의 각 비트는 e의 대응 비트의 반대값이다. 성분별 처리, T가 벡터일 때.
이항 비트 연산
전제조건 결론 설명
e1: T
e2: T
S는 AbstractInt, i32, 또는 u32
T는 S 또는 vecN<S>
e1 | e2: T 비트 OR. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S는 AbstractInt, i32, 또는 u32
T는 S 또는 vecN<S>
e1 & e2: T 비트 AND. 성분별 처리, T가 벡터일 때.
e1: T
e2: T
S는 AbstractInt, i32, 또는 u32
T는 S 또는 vecN<S>
e1 ^ e2: T 비트 XOR. 성분별 처리, T가 벡터일 때.
비트 시프트 표현식
전제조건 결론 설명
e1: T
e2: TS
S는 i32 또는 u32
TS 또는 vecN<S>
TSTS일 때 u32, 아니면 vecN<u32>
e1 << e2: T 왼쪽 시프트(시프트 값은 구체):

e1을 왼쪽으로 시프트하며, 하위 비트에는 0을 채우고, 상위 비트는 버린다.

시프트할 비트 수는 e2의 값, e1의 비트 폭으로 나눈 나머지.
e2e1의 비트 폭 이상이면:

셰이더 실행 시작 전에 e1, e2가 모두 정해진 경우, 결과값이 오버플로우되어선 안 됨:

성분별 처리, T가 벡터일 때.

e1: T
e2: TS
T는 AbstractInt 또는 vecN<AbstractInt>
TST가 AbstractInt일 때 u32, 아니면 vecN<u32>
e1 << e2: T 왼쪽 시프트(시프트 값은 추상):

e1을 왼쪽으로 시프트하며, 하위 비트에는 0을 채우고, 상위 비트는 버린다.

시프트할 비트 수는 e2의 값이다.

e1의 상위 e2+1 비트는 반드시 동일해야 한다. 아니면 오버플로우가 발생한다.

참고: 이 조건은 버려지는 비트가 원래 값의 부호 비트와 같아야 하며, 결과값의 부호 비트와도 같아야 함을 의미한다.

성분별 처리, T가 벡터일 때.

e1: T
e2: TS
S는 i32 또는 u32
TS 또는 vecN<S>
TSTS일 때 u32, 아니면 vecN<u32>
e1 >> e2: T 오른쪽 시프트(시프트 값은 구체):

e1을 오른쪽으로 시프트하며, 하위 비트는 버린다.

S가 부호 없는 타입이면, 상위 비트에 0을 채운다.

S가 부호 있는 타입이면:

  • e1이 음수면, 삽입되는 각 비트는 1이며, 결과도 음수이다.

  • 그 외에는 삽입되는 각 비트가 0이다.

시프트할 비트 수는 e2의 값, e1의 비트 폭으로 나눈 나머지.

e2e1의 비트 폭 이상이면:

성분별 처리, T가 벡터일 때.

e1: T
e2: TS
T는 AbstractInt 또는 vecN<AbstractInt>
TST가 AbstractInt일 때 u32, 아니면 vecN<u32>
e1 >> e2: T 오른쪽 시프트(추상):

e1을 오른쪽으로 시프트하며, 하위 비트는 버린다.

e1이 음수면 삽입되는 각 비트는 1이고, 결과도 음수임. 아니면 삽입되는 각 비트는 0.

시프트할 비트 수는 e2의 값이다.

성분별 처리, T가 벡터일 때.

8.10. 함수 호출 표현식

함수 호출 표현식은 함수 호출을 실행하며, 호출된 함수가 반환 타입을 가진다. 호출된 함수가 값을 반환하지 않으면, 함수 호출문을 사용해야 한다. § 9.5 함수 호출문 참고.

8.11. 변수 식별자 표현식

변수 이름에서 참조 얻기
전제조건 결론 설명
v식별자이며 해석시 스코프 내의 변수로 주소 공간 AS에 선언됨 스토어 타입 T접근 모드 AM을 가짐 v: ref<AS,T,AM> 이름이 v인 변수의 메모리에 대한 참조가 결과임.

8.12. 형식 매개변수 표현식

함수에 형식 매개변수로 선언된 식별자의 값 얻기
전제조건 결론 설명
a식별자이며 해석시 스코프 내 형식 매개변수 선언(타입 T)으로 해석됨 a: T 이 함수 인스턴스를 호출하는 호출 위치에서 제공된 함수 인자 값이 결과임.

8.13. 주소 연산자 표현식

주소 연산자는 참조를 해당 포인터로 변환합니다.

참조에서 포인터 얻기
전제조건 결론 설명
r: ref<AS,T,AM> &r: ptr<AS,T,AM> 결과는 참조 값 r와 동일한 메모리 뷰에 대응하는 포인터 값입니다.

r잘못된 메모리 참조라면, 결과 포인터도 잘못된 메모리 참조입니다.

AShandle 주소 공간이면 셰이더 생성 오류입니다.

r벡터 성분 참조라면 셰이더 생성 오류입니다.

8.14. 간접 참조 표현식

간접 참조 연산자는 포인터를 해당 참조로 변환합니다.

포인터에서 참조 얻기
전제조건 결론 설명
p: ptr<AS,T,AM> *p: ref<AS,T,AM> 결과는 포인터 값 p와 동일한 메모리 뷰에 대응하는 참조 값입니다.

p잘못된 메모리 참조라면, 결과 참조도 잘못된 메모리 참조입니다.

8.15. 값 선언을 위한 식별자 표현식

const-, override-, let 선언 식별자 값 얻기
전제조건 결론 설명
c식별자이며 해석시 스코프 내 const 선언(타입 T)으로 해석됨 c: T 초기화자 표현식으로 계산된 값이 결과입니다. 표현식은 상수 표현식이며, 셰이더 생성 시점에 평가됩니다.
c식별자이며 해석시 스코프 내 override 선언(타입 T)으로 해석됨 c: T 파이프라인 생성에서 상수 ID에 대해 값이 지정된 경우, 결과는 해당 값입니다. 이 값은 파이프라인 인스턴스마다 다를 수 있습니다.

그렇지 않으면, 결과는 초기화자 표현식으로 계산된 값입니다. 파이프라인 오버라이드 상수는 모듈 범위에 나타나므로, 셰이더 실행 전 평가됩니다.

참고: API 호출에서 초기 값이 지정되지 않고 let 선언에 초기화자가 없으면 파이프라인 생성이 실패합니다.

c식별자이며 해석시 스코프 내 let 선언(타입 T)으로 해석됨 c: T 초기화자 표현식으로 계산된 값이 결과입니다. let 선언은 함수 본문 내에 나타나며, 초기화자는 제어 흐름이 해당 선언에 도달할 때마다 평가됩니다.

8.16. 열거형 표현식

열거형 표현식
전제조건 결론 설명
e식별자이며 해석시 미리 선언된 열거자열거형 타입 E에 속함 e : E § 6.3.1 미리 선언된 열거자 참고

8.17. 타입 표현식

타입 표현식
전제조건 결론 설명
t식별자이며 해석시 미리 선언된 타입으로 해석됨 t : AllTypes § 6.9 미리 선언된 타입 및 타입 생성자 요약 참고
a식별자이며 해석시 타입 별칭으로 해석됨. a : AllTypes 추가적으로, a는 별칭된 타입을 나타냅니다.
s식별자이며 해석시 구조체 타입 선언으로 해석됨. s : AllTypes 추가적으로, s는 해당 구조체 타입을 나타냅니다.
tg식별자이며 해석시 타입 생성자로 해석됨

e1: T1
...
eN: TN

tg _template_args_start
e1,
...,
eN
_template_args_end
: AllTypes
타입 생성자는 요구하는 템플릿 파라미터 규칙 및 결과 타입 결정 방식을 정의합니다.

표현식 e1~eN은 해당 타입 생성자의 템플릿 파라미터입니다.

예를 들어 vec2<f32>2성분 벡터 타입입니다(f32).

§ 6.9 미리 선언된 타입 및 타입 생성자 요약 참고.

참고: 두 변형의 차이는 eN 뒤에 쉼표가 있느냐 뿐입니다.

tg _template_args_start
e1,
...,
eN,
_template_args_end
: AllTypes

8.18. 표현식 문법 요약

식별자call_phrase의 첫 번째 토큰일 때, 다음 중 하나입니다:

선언 및 스코프 규칙에 따라 이 이름들은 항상 구분됩니다.

primary_expression :

template_elaborated_ident

| call_expression

| literal

| paren_expression

call_expression :

call_phrase

참고: call_expression 규칙은 호출 표현식에 타입 검사가 적용되도록 보장합니다.

call_phrase :

template_elaborated_ident argument_expression_list

paren_expression :

'(' expression ')'

argument_expression_list :

'(' expression_comma_list ? ')'

expression_comma_list :

expression ( ',' expression ) * ',' ?

component_or_swizzle_specifier :

'[' expression ']' component_or_swizzle_specifier ?

| '.' member_ident component_or_swizzle_specifier ?

| '.' swizzle_name component_or_swizzle_specifier ?

unary_expression :

singular_expression

| '-' unary_expression

| '!' unary_expression

| '~' unary_expression

| '*' unary_expression

| '&' unary_expression

singular_expression :

primary_expression component_or_swizzle_specifier ?

lhs_expression :

core_lhs_expression component_or_swizzle_specifier ?

| '*' lhs_expression

| '&' lhs_expression

core_lhs_expression :

ident _disambiguate_template

| '(' lhs_expression ')'

multiplicative_expression :

unary_expression

| multiplicative_expression multiplicative_operator unary_expression

multiplicative_operator :

'*'

| '/'

| '%'

additive_expression :

multiplicative_expression

| additive_expression additive_operator multiplicative_expression

additive_operator :

'+'

| '-'

shift_expression :

additive_expression

| unary_expression _shift_left unary_expression

| unary_expression _shift_right unary_expression

relational_expression :

shift_expression

| shift_expression _less_than shift_expression

| shift_expression _greater_than shift_expression

| shift_expression _less_than_equal shift_expression

| shift_expression _greater_than_equal shift_expression

| shift_expression '==' shift_expression

| shift_expression '!=' shift_expression

short_circuit_and_expression :

relational_expression

| short_circuit_and_expression '&&' relational_expression

short_circuit_or_expression :

relational_expression

| short_circuit_or_expression '||' relational_expression

binary_or_expression :

unary_expression

| binary_or_expression '|' unary_expression

binary_and_expression :

unary_expression

| binary_and_expression '&' unary_expression

binary_xor_expression :

unary_expression

| binary_xor_expression '^' unary_expression

bitwise_expression :

binary_and_expression '&' unary_expression

| binary_or_expression '|' unary_expression

| binary_xor_expression '^' unary_expression

expression :

relational_expression

| short_circuit_or_expression '||' relational_expression

| short_circuit_and_expression '&&' relational_expression

| bitwise_expression

8.19. 연산자 우선순위와 결합성

이 전체 절은 비규범적입니다.

WGSL 우변 표현식에서의 연산자 우선순위와 결합성은 요약된 문법에서 도출됩니다. 우변 표현식들은 연산자를 그룹으로 묶어 구성하며, 다음 다이어그램으로 예시됩니다:

연산자 우선순위 및 결합성 그래프

가독성을 높이기 위해, 다음 그룹들은 다른 그룹과 결합하지 않습니다:

그리고 다음 그룹은 자기 자신과 결합하지 않습니다:

위 두 그룹 섹션을 모두 결합하려면 괄호로 명시적으로 관계를 지정해야 합니다.
다음은 이러한 규칙이 표현식을 주석에서 잘못되게 만드는 예시입니다:

예시: 연산자 우선순위 특이 케이스
let a = x & (y ^ (z | w)); // 잘못됨: x & y ^ z | w
let b = (x + y) << (z >= w); // 잘못됨: x + y << z >= w
let c = x < (y > z); // 잘못됨: x < y > z
let d = x && (y || z); // 잘못됨: x && y || z

표현식의 묵시적 괄호는 우선순위에 따라 결정되며, 결합력이 더 강한 연산자는 약한 연산자와 함께 있을 때 괄호로 감싸진 것처럼 동작합니다. 예를 들어 곱셈 연산자가 덧셈보다 결합력이 강하므로 a + b * c(a + (b * c))로 해석됩니다. 또한 결합성에 따라 묵시적 괄호의 방향이 결정됩니다. 예를 들어 좌측 결합이면 a + b + c((a + b) + c)로, 우측 결합이면 * * a(* (* a))로 해석됩니다.

아래 표는 연산자 우선순위, 결합성, 결합 대상을 가장 강한 것부터 약한 것 순으로 정리한 것입니다. 결합(Binding) 열의 값이 "All above"면 해당 연산자는 그보다 강한 모든 표현식을 포함할 수 있음을 의미합니다. "Unary"면, 단항보다 약하지만 해당 행 연산자보다 강한 연산자는 괄호로 결합해야 합니다. 이 열은 연산자를 선형적으로 나열하기 위해 필요합니다.

우변 표현식의 연산자 우선순위, 결합성, 결합 대상 요약(강한 것부터 약한 것 순)
이름 연산자 결합성 결합 대상
괄호 (...)
기본 a(), a[], a.b 좌측 결합
단항 -a, !a, ~a, *a, &a 우측 결합 상위 모두
곱셈 a*b, a/b, a%b 좌측 결합 상위 모두
덧셈 a+b, a-b 좌측 결합 상위 모두
시프트 a<<b, a>>b 괄호 필요 단항
관계형 a<b, a>b, a<=b, a>=b, a==b, a!=b 괄호 필요 상위 모두
이항 AND a&b 좌측 결합 단항
이항 XOR a^b 좌측 결합 단항
이항 OR a|b 좌측 결합 단항
단락 평가 AND a&&b 좌측 결합 관계형
단락 평가 OR a||b 좌측 결합 관계형

9. 문장

문장은 실행을 제어하는 프로그램 단위입니다. 문장은 일반적으로 순차적으로 실행되지만, 제어 흐름 문장은 프로그램을 비순차적으로 실행할 수 있습니다.

9.1. 복합 문장

복합 문장은 중괄호로 묶인 0개 이상의 문장 시퀀스입니다. 그 안에 선언이 포함될 경우, 해당 식별자는 다음 문장 시작부터 복합 문장 끝까지 스코프 내에 존재합니다.

compound_statement :

attribute * '{' statement * '}'

continuing_compound_statement는 복합 문장의 특수 형태로, continuing 문장의 본문을 구성하며, 마지막에 break-if 문장을 선택적으로 포함할 수 있습니다.

9.2. 대입 문장

대입은 표현식을 평가하고, 옵션에 따라 메모리에 저장하여 변수의 내용을 갱신합니다.

assignment_statement :

lhs_expression ( '=' | compound_assignment_operator ) expression

| '_' '=' expression

연산자 토큰 왼쪽의 텍스트를 좌변, 연산자 토큰 오른쪽의 표현식을 우변이라 합니다.

9.2.1. 단순 대입

대입단순 대입이 되는 경우는, 좌변이 표현식이고, 연산자가 등호('=') 토큰일 때입니다. 이 경우, 우변의 값이 좌변이 참조하는 메모리에 기록됩니다.

전제조건 문장 설명
e: T,
T구체 생성가능 타입,
r: ref<AS,T,AM>,
AS가 쓰기 가능한 주소 공간,
접근 모드 AMwrite 또는 read_write
r = e r을 평가하고, e를 평가한 뒤, e의 계산 결과를 r이 참조하는 메모리 위치에 기록합니다.

참고: 참조가 잘못된 메모리 참조일 경우, 기록이 수행되지 않거나 예상과 다른 위치에 기록될 수 있습니다.

가장 단순한 경우는 좌변이 변수 이름일 때입니다. 다른 경우는 § 6.4.8 참조 및 포인터 값 생성을 참고하세요.

예시: 대입문
struct S {
    age: i32,
    weight: f32
}
var<private> person: S;

fn f() {
    var a: i32 = 20;
    a = 30;           // 'a' 내용물을 30으로 교체.

    person.age = 31;  // person 변수의 나이 필드에 31 기록.

    var uv: vec2<f32>;
    uv.y = 1.25;      // uv의 두 번째 성분에 1.25 저장.

    let uv_x_ptr: ptr<function,f32> = &uv.x;
    *uv_x_ptr = 2.5;  // uv의 첫 번째 성분에 2.5 저장.

    var sibling: S;
    // person 변수의 내용을 sibling 변수에 복사.
    sibling = person;
}

9.2.2. 가짜 대입

대입가짜 대입이 되는 경우는, 좌변이 언더스코어('_') 토큰일 때입니다. 이 경우 우변은 평가만 되고, 결과는 무시됩니다.

전제조건 문장 설명
e: T,
T생성가능, 포인터 타입, 텍스처 타입, 또는 샘플러 타입
_ = e e를 평가합니다.

참고: 평가 결과값은 저장되지 않습니다. _ 토큰은 식별자가 아니므로, 표현식에서 사용할 수 없습니다.

가짜 대입은 다음과 같은 경우에 유용합니다:

예시: 필요 없는 함수 결과를 버릴 때 가짜 대입 사용
var<private> counter: i32;

fn increment_and_yield_previous() -> i32 {
  let previous = counter;
  counter = counter + 1;
  return previous;
}

fn user() {
  // counter를 증가시키지만 결과는 사용하지 않음.
  _ = increment_and_yield_previous();
}
예시: 바인딩만 점유하고 사용하지 않을 때 가짜 대입 사용
struct BufferContents {
    counter: atomic<u32>,
    data: array<vec4<f32>>
}
@group(0) @binding(0) var<storage> buf: BufferContents;
@group(0) @binding(1) var t: texture_2d<f32>;
@group(0) @binding(2) var s: sampler;

@fragment
fn shade_it() -> @location(0) vec4<f32> {
  // buf, t, s를 셰이더 인터페이스에 포함시키되 실제로 사용하지 않음.
  _ = &buf;
  _ = t;
  _ = s;
  return vec4<f32>();
}

9.2.3. 복합 대입

대입복합 대입이 되는 경우는, 좌변이 표현식이고, 연산자가 복합 대입 연산자 중 하나일 때입니다.

compound_assignment_operator :

'+='

| '-='

| '*='

| '/='

| '%='

| '&='

| '|='

| '^='

| _shift_right_assign

| _shift_left_assign

각 문장의 타입 요구사항, 의미론, 동작은 복합 대입이 아래 표와 같이 확장되는 것처럼 정의됩니다. 단,

문장 확장
e1 += e2 e1 = e1 + (e2)
e1 -= e2 e1 = e1 - (e2)
e1 *= e2 e1 = e1 * (e2)
e1 /= e2 e1 = e1 / (e2)
e1 %= e2 e1 = e1 % (e2)
e1 &= e2 e1 = e1 & (e2)
e1 |= e2 e1 = e1 | (e2)
e1 ^= e2 e1 = e1 ^ (e2)
e1 >>= e2 e1 = e1 >> (e2)
e1 <<= e2 e1 = e1 << (e2)

참고: 이 문법은 복합 대입가짜 대입이 되는 것은 허용하지 않습니다.

참고: 참조 e1은 한 번만 평가되지만, 해당 메모리는 두 번 접근됩니다. 먼저 읽기 접근으로 기존 값을 얻고, 그 다음 쓰기 접근으로 갱신된 값을 저장합니다.

예시: 복합 대입
var<private> next_item: i32 = 0;

fn advance_item() -> i32 {
   next_item += 1;   // next_item에 1을 더함.
   return next_item - 1;
}

fn bump_item() {
  var data: array<f32,10>;
  next_item = 0;
  // data[0]에 5.0을 더함. advance_item()은 한 번만 호출됨.
  data[advance_item()] += 5.0;
  // 여기서 next_item은 1이 됨.
}

fn precedence_example() {
  var value = 1;
  // 복합 대입의 우변은 자체 표현식임.
  value *= 2 + 3; // value = value * (2 + 3)과 동일함.
  // 'value'는 이제 5가 됨.
}
참고: 복합 대입은 단순 대입을 사용하는 WGSL 코드로 다시 작성할 수 있습니다. 아이디어는 참조 평가 결과를 포인터로 받아 한 번만 평가하는 것입니다.
예를 들어, e1이 벡터 성분이 아닌 참조일 때,
e1+=e2;
아래와 같이 다시 쓸 수 있습니다.
{ let p = &(e1); *p = *p + (e2); }
여기서 식별자 p는 프로그램 내 다른 식별자와 중복되지 않게 선택합니다.
e1이 벡터 내부 성분에 대한 참조일 경우, 위 기법을 수정해야 합니다. WGSL에서는 주소 취득이 허용되지 않기 때문입니다. 예를 들어 ev가 벡터에 대한 참조라면, 아래 문장
ev[c] += e2;
아래와 같이 다시 쓸 수 있습니다.
{ let p = &(ev); let c0 = c; (*p)[c0] = (*p)[c0] + (e2); }
여기서 c0p 식별자는 프로그램 내 다른 식별자와 중복되지 않게 선택합니다.

9.3. 증감 문장

증가 문장은 변수의 값에 1을 더합니다. 감소 문장은 변수의 값에서 1을 뺍니다.

increment_statement :

lhs_expression '++'

decrement_statement :

lhs_expression '--'

해당 표현식은 반드시 구체 정수 스칼라 저장 타입read_write 접근 모드의 참조로 평가되어야 합니다.

전제조건 문장 설명
r : ref<AS,T,read_write>,
T구체 정수 스칼라
r++ r이 참조하는 메모리 내용에 1을 더함.
r += T(1)과 동일
r : ref<AS,T,read_write>,
T구체 정수 스칼라
r-- r이 참조하는 메모리 내용에서 1을 뺌.
r -= T(1)과 동일
예시: 증가와 감소
fn f() {
    var a: i32 = 20;
    a++;
    // 이제 a의 값은 21
    a--;
    // 이제 a의 값은 20
}

9.4. 제어 흐름

제어 흐름 문장은 프로그램을 비순차적으로 실행시킬 수 있습니다.

9.4.1. if 문장

if 문장은 조건 표현식의 평가에 따라 최대 하나의 복합 문장을 조건부로 실행합니다.

if 문은 if 절, 0개 이상의 else if 절, 선택적 else 절로 구성됩니다.

if_statement :

attribute * if_clause else_if_clause * else_clause ?

if_clause :

'if' expression compound_statement

else_if_clause :

'else' 'if' expression compound_statement

else_clause :

'else' compound_statement

타입 규칙 전제조건: 각 ifelse if 절의 표현식은 반드시 bool 타입이어야 합니다.

if 문장은 다음과 같이 실행됩니다:

9.4.2. switch 문장

switch 문장은 선택자 표현식의 평가에 따라 여러 case 절 또는 default 절 중 하나로 제어를 이동시킵니다.

switch_statement :

attribute * 'switch' expression switch_body

switch_body :

attribute * '{' switch_clause + '}'

switch_clause :

case_clause

| default_alone_clause

case_clause :

'case' case_selectors ':' ? compound_statement

default_alone_clause :

'default' ':' ? compound_statement

case_selectors :

case_selector ( ',' case_selector ) * ',' ?

case_selector :

'default'

| expression

case 절'case' 토큰 다음에 쉼표로 구분된 case 선택자 리스트와 복합 문장 형태의 본문이 옵니다.

default-alone 절'default' 토큰 뒤에 복합 문장 형태의 본문이 옵니다.

default 절은 다음 중 하나입니다:

각 switch 문장은 반드시 하나의 default 절를 가져야 합니다.

'default' 토큰은 하나의 case_selector 리스트에 두 번 이상 나타나면 안 됩니다.

타입 규칙 전제조건: 하나의 switch 문장에 대해 선택자 표현식과 모든 case 선택자 표현식은 반드시 동일한 구체 정수 스칼라 타입이어야 합니다.

case_selectors의 표현식들은 반드시 const-expression이어야 합니다.

동일 switch 문장 내에서 서로 다른 case 선택자 표현식이 동일한 값을 가지면 안 됩니다.

선택자 값이 case_selector 리스트의 어떤 표현식과 같으면, 해당 case 절의 본문으로 제어가 이동합니다. 선택자 값이 어떤 case 선택자 값과도 같지 않으면 default 절의 본문으로 제어가 이동합니다.

절 본문의 끝에 도달하면, 제어는 switch 문장 바로 뒤의 첫 번째 문장으로 이동합니다.

절 본문 내 문장이 선언이면, 복합 문장의 스코프수명 규칙을 따릅니다. 즉, 본문은 문장 시퀀스이고, 그 중 하나가 선언이면 해당 선언의 스코프는 다음 문장부터 본문 끝까지입니다. 선언은 도달 시 실행되어 변수 또는 의 새 인스턴스를 생성 및 초기화합니다.

예시: WGSL switch
var a : i32;
let x : i32 = generateValue();
switch x {
  case 0: {      // 콜론은 선택적입니다
    a = 1;
  }
  default {      // default는 마지막에 올 필요가 없습니다
    a = 2;
  }
  case 1, 2, {   // 여러 선택자 값을 사용할 수 있습니다
    a = 3;
  }
  case 3, {      // 마지막 쉼표는 선택적입니다
    a = 4;
  }
  case 4 {
    a = 5;
  }
}
예시: default가 결합된 WGSL switch
const c = 2;
var a : i32;
let x : i32 = generateValue();
switch x {
  case 0: {
    a = 1;
  }
  case 1, c {       // case 선택자에 상수 표현식 사용 가능
    a = 3;
  }
  case 3, default { // default 키워드는 다른 절과 결합될 수 있음
    a = 4;
  }
}

9.4.3. loop 문장

loop_statement :

attribute * 'loop' attribute * '{' statement * continuing_statement ? '}'

loop 문장은 loop 본문을 반복 실행합니다; loop 본문은 복합 문장으로 지정됩니다. loop 본문의 각 실행을 반복(iteration)이라고 합니다.

이 반복은 break 또는 return 문장에 의해 중단될 수 있습니다.

옵션으로, loop 본문의 마지막 문장은 continuing 문장일 수 있습니다.

loop가 무한 반복될 경우 동적 오류가 발생합니다. 이 경우 반복이 조기에 종료되거나, 기타 비지역적 효과가 발생하거나 디바이스 손실이 발생할 수 있습니다.

loop 본문 내 문장이 선언이면, 복합 문장의 스코프수명 규칙을 따릅니다. 즉, loop 본문은 문장 시퀀스이고, 그 중 하나가 선언이면 해당 선언의 스코프는 다음 문장부터 loop 본문 끝까지입니다. 선언은 도달 시마다 실행되어 변수 또는 의 새 인스턴스를 생성 및 초기화합니다.

참고: loop 문장은 특수 구조이므로, 일반적으로 forwhile 문장을 사용할 수 있습니다. loop 문장은 다른 셰이더 언어와 가장 큰 차이점 중 하나입니다.

이 설계는 컴파일된 코드에서 흔히 볼 수 있는 반복 패턴을 직접적으로 표현합니다. 특히, loop body 끝에 반복 갱신문을 배치하면 loop 본문에서 정의된 값을 자연스럽게 사용할 수 있습니다.

예시: for 반복문
var a: i32 = 2;
for (var i: i32 = 0; i < 4; i++) {
  a *= 2;
}
예시: loop 반복문
var a: i32 = 2;
var i: i32 = 0;      // <1>
loop {
  if i >= 4 { break; }

  a = a * 2;

  i++;
}
예시: continue가 있는 for 반복문
var a: i32 = 2;
let step: i32 = 1;
for (var i: i32 = 0; i < 4; i += step) {
  if (i % 2 == 0) { continue; }
  a *= 2;
}
예시: continue가 있는 loop 반복문
var a: i32 = 2;
var i: i32 = 0;
loop {
  if i >= 4 { break; }

  let step: i32 = 1;

  i = i + step;
  if i % 2 == 0 { continue; }

  a = a * 2;
}
예시: continue와 continuing이 있는 loop 반복문
var a: i32 = 2;
var i: i32 = 0;
loop {
  if i >= 4 { break; }

  let step: i32 = 1;

  if i % 2 == 0 { continue; }

  a = a * 2;

  continuing {   // <2>
    i = i + step;
  }
}

9.4.4. for 문장

for_statement :

attribute * 'for' '(' for_header ')' compound_statement

for_header :

for_init ? ';' expression ? ';' for_update ?

for_init :

variable_or_value_statement

| variable_updating_statement

| func_call_statement

for_update :

variable_updating_statement

| func_call_statement

for 문장은 for (초기화; 조건; 갱신부) { 본문 } 형태를 가지며, 동일한 본문을 갖는 loop 문장의 구문적 설탕입니다. 추가적으로:

for 반복문의 초기화는 반복문 실행 전 한 번만 실행됩니다. 선언이 초기화에 등장하면, 해당 식별자스코프 내에 본문 끝까지 존재합니다. 본문 내 선언과 달리, 초기화 선언은 각 반복마다 재초기화되지 않습니다.

조건, 본문, 갱신부는 해당 순서대로 실행되어 loop 반복을 형성합니다. 본문복합 문장의 특수 형태입니다. 본문 내 선언의 식별자는 스코프 내에 다음 문장 시작부터 본문 끝까지 존재합니다. 선언은 매번 도달할 때 실행되므로, 각 반복마다 변수나 상수의 새 인스턴스를 생성 및 재초기화합니다.

예시: For를 Loop로 변환 - before
var a: i32 = 2;
for (var i: i32 = 0; i < 4; i++) {
  if a == 0 {
    continue;
  }
  a = a + 2;
}

변환 결과:

예시: For를 Loop로 변환 - after
var a: i32 = 2;
{ // 반복 변수 i를 위한 새로운 스코프 도입
  var i: i32 = 0;
  loop {
    if !(i < 4) {
      break;
    }

    if a == 0 {
      continue;
    }
    a = a + 2;

    continuing {
      i++;
    }
  }
}

for 반복문이 무한 반복될 경우 동적 오류가 발생합니다. 이 경우 반복이 조기에 종료되거나, 기타 비지역적 효과가 발생하거나 디바이스 손실이 발생할 수 있습니다.

9.4.5. while 문장

while_statement :

attribute * 'while' expression compound_statement

while 문장은 조건에 따라 반복되는 반복문입니다. 각 반복 iteration 시작 시, bool 조건이 평가됩니다. 조건이 false일 경우 while 반복문은 실행을 종료합니다. 그렇지 않으면 나머지 반복이 실행됩니다.

타입 규칙 전제조건: 조건은 반드시 bool 타입이어야 합니다.

while 반복문은 loop 또는 for 문장의 구문적 설탕으로 볼 수 있습니다. 아래 형태의 문장은 모두 동일합니다:

while 반복문이 무한 반복될 경우 동적 오류가 발생합니다. 이 경우 반복이 조기에 종료되거나, 기타 비지역적 효과가 발생하거나 디바이스 손실이 발생할 수 있습니다.

9.4.6. break 문장

break_statement :

'break'

break 문장은 가장 가까운 감싸는 loop 또는 switch 문장의 본문 바로 뒤로 제어를 이동시켜, 반복 또는 switch 문장 실행을 종료합니다.

break 문장은 반드시 loop, for, while, switch 문장 내에서만 사용할 수 있습니다.

break 문장은 반복문의 continuing 문장을 빠져나가도록 배치되어서는 안 됩니다. 대신 break-if 문장을 사용하세요.

예시: continuing 절에서 break하는 잘못된 WGSL 반복문
var a: i32 = 2;
var i: i32 = 0;
loop {
  let step: i32 = 1;

  if i % 2 == 0 { continue; }

  a = a * 2;

  continuing {
    i = i + step;
    if i >= 4 { break; } // 잘못됨. break-if를 사용해야 함.
  }
}

9.4.7. break-if 문장

break_if_statement :

'break' 'if' expression ';'

break-if 문장은 bool 조건을 평가합니다. 조건이 true이면, 제어는 가장 가까운 loop 문장의 본문 바로 뒤로 이동하여 해당 loop 실행을 종료합니다.

타입 규칙 전제조건: 조건은 반드시 bool 타입이어야 합니다.

참고: break-if 문장은 continuing 문장 본문의 마지막 문장으로만 등장할 수 있습니다.

예시: continuing 절에서 break-if를 사용하는 올바른 WGSL 반복문
var a: i32 = 2;
var i: i32 = 0;
loop {
  let step: i32 = 1;

  if i % 2 == 0 { continue; }

  a = a * 2;

  continuing {
    i = i + step;
    break if i >= 4;
  }
}

9.4.8. continue 문장

continue_statement :

'continue'

continue 문장은 가장 가까운 loop에서 제어를 다음 위치로 이동시킵니다:

continue 문장은 반드시 loop, for, while 문장에서만 사용할 수 있습니다. continue 문장은 반드시 감싸는 continuing 문장으로 제어를 이동시키는 위치에 올 수 없습니다. (continuing 문장으로 이동하는 경우 forward 분기입니다.)

continue 문장은 반드시 targeted continuing 문장에서 사용하는 선언을 건너뛰게 배치되어서는 안 됩니다.

참고: continuecontinuing 문장 내에서, 그 continuing 문장 내 또 다른 loop에서만 제어 흐름을 이동시킬 때 사용할 수 있습니다. 즉, 현재 실행 중인 continuing 문장 시작으로 제어를 이동시키는 용도로는 사용할 수 없습니다.

예시: 선언을 건너뛰는 잘못된 continue
var i: i32 = 0;
loop {
  if i >= 4 { break; }
  if i % 2 == 0 { continue; } // <3>

  let step: i32 = 2;

  continuing {
    i = i + step;
  }
}

9.4.9. continuing 문장

continuing_statement :

'continuing' continuing_compound_statement

continuing_compound_statement :

attribute * '{' statement * break_if_statement ? '}'

continuing 문장은 loop 반복 끝에 실행되는 복합 문장을 지정합니다. 이 구문은 선택적입니다.

복합 문장에는 어느 중첩 단계에서도 return이 올 수 없습니다.

9.4.10. return 문장

return_statement :

'return' expression ?

return 문장은 현재 함수의 실행을 종료합니다. 함수가 엔트리 포인트일 경우, 현재 셰이더 호출이 종료됩니다. 그렇지 않으면, 현재 함수 호출의 호출 위치 평가 이후의 다음 표현식 또는 문장으로 실행이 이어집니다.

함수에 반환 타입이 없으면 return 문장은 선택사항입니다. 이런 함수에서 return 문을 제공할 경우 반드시 값을 제공해선 안 됩니다. 그렇지 않으면, 반드시 반환값 표현식이 있어야 하며, 이를 반환값이라 합니다. 이 경우 해당 함수 호출 위치의 평가 결과가 반환값이 됩니다. 반환값의 타입은 함수의 반환 타입과 반드시 일치해야 합니다.

9.4.11. discard 문장

discard 문장은 현재 호출을 helper 호출로 변환하여 프래그먼트를 버립니다. discard 문장은 반드시 fragment 셰이더 단계에서만 사용할 수 있습니다.

정확히 말하면 discard 문장을 실행하면 다음이 발생합니다:

discard 문장 실행 전에 실행된 문장만 관찰 가능한 효과를 가집니다.

참고: discard 문장은 fragment 단계의 어떤 함수에서든 실행될 수 있으며, 효과는 같습니다: 해당 프래그먼트는 버려집니다.

예시: discard 문장을 사용해 프래그먼트 버리기
@group(0) @binding(0)
var<storage, read_write> will_emit_color : u32;

fn discard_if_shallow(pos: vec4<f32>) {
  if pos.z < 0.001 {
    // 이 문장이 실행되면, will_emit_color 변수는 절대 1이 되지 않는다.
    // helper 호출은 공유 메모리에 기록하지 않으므로.
    discard;
  }
  will_emit_color = 1;
}

@fragment
fn main(@builtin(position) coord_in: vec4<f32>)
  -> @location(0) vec4<f32>
{
  discard_if_shallow(coord_in);

  // 값을 1로 설정하고 빨간색을 내보내지만, helper 함수가 discard 문장을 실행하지 않은 경우에만 해당됨.
  will_emit_color = 1;
  return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}

9.5. 함수 호출 문장

func_call_statement :

call_phrase

함수 호출 문장은 함수 호출을 실행합니다.

호출된 함수에 must_use 속성이 있으면 셰이더 생성 오류가 발생합니다.

참고: 함수가 값을 반환하고, must_use 속성이 없다면 그 값은 무시됩니다.

9.6. 문장 문법 요약

statement 규칙은 함수 본문 내 대부분 위치에서 사용 가능한 문장에 일치합니다.

statement :

';'

| return_statement ';'

| if_statement

| switch_statement

| loop_statement

| for_statement

| while_statement

| func_call_statement ';'

| variable_or_value_statement ';'

| break_statement ';'

| continue_statement ';'

| 'discard' ';'

| variable_updating_statement ';'

| compound_statement

| assert_statement ';'

variable_updating_statement :

assignment_statement

| increment_statement

| decrement_statement

추가적으로, 특정 문장은 아주 특정한 문맥에서만 사용될 수 있습니다:

9.7. 문장 동작 분석

9.7.1. 규칙

제어 흐름에 영향을 주는 일부 문장은 특정 문맥에서만 유효합니다. 예를 들어, continueloop, for, 또는 while 안에서만 유효합니다. 또한, 균일성 분석(see § 15.2 균일성)에서는 제어 흐름이 여러 방식으로 문장을 빠져나갈 수 있는지 파악해야 합니다.

이 두 목적은 문장 실행 동작 요약 시스템으로 달성됩니다. 동작 분석은 각 문장을 평가 완료 후 실행이 이어지는 가능한 방식을 집합으로 요약합니다. 값과 표현식의 타입 분석처럼, 동작 분석도 하향식(bottom up)으로 진행됩니다: 먼저 기본 문장들의 동작을 결정한 뒤, 결합 규칙을 적용해 상위 구조의 동작을 결정합니다.

동작(behavior)은 집합이며, 요소는 다음일 수 있습니다:

각 요소는 복합 문장을 탈출하는 방법(키워드를 통해 또는 "Next"로 다음 문장으로 넘어감)에 대응됩니다.

"s: B"는 s가 동작 규칙을 준수하며 동작 B를 가진다는 의미입니다.

각 함수에 대해:

각 함수에 동작을 할당합니다: 이는 본체의 동작이며("Return"은 "Next"로 치환), 본체를 일반 문장처럼 취급합니다. 위 규칙의 결과로, 함수 동작은 항상 {}, 혹은 {Next}입니다.

동작 분석은 각 문장 및 함수에 대해 비어있지 않은 동작을 결정할 수 있어야 합니다.

문장 동작 분석 및 검증 규칙
문장 전제조건 결과 동작
빈 문장 {Next}
{s} s: B B
s1 s2

참고: s1은 종종 세미콜론으로 끝납니다.

s1: B1
Next가 B1에 있음
s2: B2
(B1∖{Next}) ∪ B2
s1: B1
Next가 B1에 없음
s2: B2
B1
var x:T; {Next}
let x = e; {Next}
var x = e; {Next}
x = e; {Next}
_ = e; {Next}
f(e1, ..., en); f 동작 B를 가짐 B
return; {Return}
return e; {Return}
discard; {Next}
break; {Break}
break if e; {Break, Next}
continue; {Continue}
const_assert e; {Next}
if e s1 else s2 s1: B1
s2: B2
B1B2
loop {s1 continuing {s2}} s1: B1
s2: B2
{Continue, Return}이 B2에 없음
Break가 (B1B2)에 없음
(B1B2)∖{Continue, Next}
s1: B1
s2: B2
{Continue, Return}이 B2에 없음
Break가 (B1B2)에 있음
(B1B2 ∪ {Next})∖{Break, Continue}
switch e {case c1: s1 ... case cn: sn} s1: B1
...
sn: Bn
Break가 (B1 ∪ ... ∪ Bn)에 없음
B1 ∪ ... ∪ Bn
s1: B1
...
sn: Bn
Break가 (B1 ∪ ... ∪ Bn)에 있음
(B1 ∪ ... ∪ Bn ∪ {Next})∖Break

참고: ∪는 집합 합집합, ∖는 집합 차집합 연산입니다.

참고: 빈 문장은 loop의 본문이 비었거나, for 반복문이 초기화 또는 갱신 문장이 없는 경우에 발생합니다.

이 분석을 위해:

내장 함수는 {Next} 동작을 가집니다. 위 표에 없는 연산자 적용도 동일 피연산자를 갖는 함수 호출과 동일하게 {Next} 동작을 가집니다.

함수의 동작은 반드시 위 규칙을 만족해야 합니다.

참고: 표현식의 동작을 분석할 필요는 없습니다. 항상 {Next}이거나, 이전에 분석된 함수가 오류를 발생시켰기 때문입니다.

9.7.2. 참고

이 절은 참고용이며 비규범적입니다.

동작 분석은 다음과 같은 경우 프로그램을 거부할 수 있습니다 (위의 요구사항을 재진술):

이 분석은 호출 그래프를 하향식(bottom-up)으로 분석하면 선형 시간에 실행할 수 있습니다(함수 호출의 동작이 함수 코드에 따라 달라질 수 있으므로).

9.7.3. 예시

다음은 동작 분석이 어떻게 적용되는지 보여주는 예시입니다:

예시: 명백히 죽은 코드는 허용됨
fn simple() -> i32 {
  var a: i32;
  return 0;  // 동작: {Return}
  a = 1;     // 유효함, 정적으로 도달 불가한 코드.
             //   문장 동작: {Next}
             //   전체 동작(순차문 때문): {Return}
  return 2;  // 유효, 정적으로 도달 불가한 코드. 동작: {Return}
} // 함수 동작: {Return}
예시: 복합 문장 지원
fn nested() -> i32 {
  var a: i32;
  {             // 복합 문장 시작.
    a = 2;      // 동작: {Next}
    return 1;   // 동작: {Return}
  }             // 복합 문장 전체 동작: {Return}
  a = 1;        // 유효, 정적으로 도달 불가한 코드.
                //   문장 동작: {Next}
                //   전체 동작(순차문 때문): {Return}
  return 2;     // 유효, 정적으로 도달 불가한 코드. 동작: {Return}
}
예시: if/then은 빈 else가 있는 것처럼 동작
fn if_example() {
  var a: i32 = 0;
  loop {
    if a == 5 {
      break;      // 동작: {Break}
    }             // if 복합 문장 전체 동작: {Break, Next},
                  //   if에 암묵적 빈 else가 있음
    a = a + 1;    // 유효, 이전 문장 동작에 "Next" 있음
  }
}
예시: if/then/else는 양쪽 동작을 모두 가짐
fn if_example() {
  var a: i32 = 0;
  loop {
    if a == 5 {
      break;      // 동작: {Break}
    } else {
      continue;   // 동작: {Continue}
    }             // if 복합 문장 전체 동작: {Break, Continue}
    a = a + 1;    // 유효, 정적으로 도달 불가한 코드.
                  //   문장 동작: {Next}
                  //   전체 동작: {Break, Continue}
  }
}
예시: if/else if/else는 중첩 if/else처럼 동작
fn if_example() {
  var a: i32 = 0;
  loop {
    // if e1 s1 else if e2 s2 else s3
    //는 아래와 동일함
    // if e1 else { if e2 s2 else s3 }
    if a == 5 {
      break;      // 동작: {Break}
    } else if a == 42 {
      continue;   // 동작: {Continue}
    } else {
      return;     // 동작: {Return}
    }             // if 복합 문장 전체 동작:
                  //   {Break, Continue, Return}
  }               // loop 복합 문장 전체 동작 {Next, Return}
}                 // 함수 전체 동작 {Next}
예시: switch의 Break는 Next가 됨
fn switch_example() {
  var a: i32 = 0;
  switch a {
    default: {
      break;   // 동작: {Break}
    }
  }            // 동작: {Next}, switch는 Break를 Next로 치환함
  a = 5;       // 유효, 이전 문장 동작에 Next 있음
}
예시: 명백히 무한 루프
fn invalid_infinite_loop() {
  loop { }     // 동작: { }. 비어 있으므로 유효하지 않음.
}
예시: discard는 루프를 종료하지 않음
fn invalid_infinite_loop() {
  loop {
    discard; // 동작 {Next}.
  }
                // 유효하지 않음, 전체 루프 동작은 { }임.
}
예시: continuing 문장이 있는 조건부 continue
fn conditional_continue() {
  var a: i32;
  loop {
    if a == 5 { break; } // 동작: {Break, Next}
    if a % 2 == 1 {      // 유효, 이전 문장 동작에 Next 있음
      continue;          // 동작: {Continue}
    }                    // 동작: {Continue, Next}
    a = a * 2;           // 유효, 이전 문장 동작에 Next 있음
    continuing {         // 유효, continuing 문장 동작은 {Next}
                         //  아래 동작 미포함:
                         //  {Break, Continue, Return}
      a = a + 1;
    }
  }                      // 전체 루프 동작은 {Next},
                         //  "Continue"와 "Next"를 흡수,
                         //  "Break"는 Next로 치환
}
예시: continuing 문장이 있는 불필요한 continue
fn redundant_continue_with_continuing() {
  var a: i32;
  loop {
    if a == 5 { break; }
    continue;   // 유효. 불필요하지만, 다음 문장으로 분기.
    continuing {
      a = a + 1;
    }
  }
}
예시: 루프 본문 끝의 continue
fn continue_end_of_loop_body() {
  for (var i: i32 = 0; i < 5; i++ ) {
    continue;   // 유효. 불필요하지만, 루프 본문 끝으로 분기.
  }             // 동작: {Next},
                //   루프가 "Continue"를 흡수,
                //   for 반복문은 항상 "Next" 추가
}
for 반복문은 조건부 break가 있는 loop로 desugar 처리됩니다. 앞선 예시에서 보았듯이, 조건부 break는 동작 {Break, Next}를 가지며, 루프 동작에 "Next"가 추가됩니다.
예시: 반환 타입이 있는 함수에는 return이 필요
fn missing_return () -> i32 {
  var a: i32 = 0;
  if a == 42 {
    return a;       // 동작: {Return}
  }                 // 동작: {Next, Return}
}                   // 오류: Next는 반환 타입이 있는 함수 본문에 유효하지 않음
예시: continue는 반드시 loop 내에 있어야 함
fn continue_out_of_loop () {
  var a: i32 = 0;
  if a > 0  {
    continue;       // 동작: {Continue}
  }                 // 동작: {Next, Continue}
}                   // 오류: Continue는 함수 본문에 유효하지 않음
동일한 예시에서 continuebreak로 바꿔도 같은 이유로 유효하지 않습니다.

10. 단언문

단언문은 bool 조건이 충족되는지 확인하는 검사입니다.

global_assert :

const_assert ';'

WGSL은 한 가지 단언문을 정의합니다: const 단언문.

const_assert :

'const_assert' expression

타입 규칙 전제조건: 해당 표현식은 반드시 bool 타입이어야 합니다.

10.1. Const 단언문

Const 단언문은 단언문으로, 해당 표현식이 false로 평가되면 셰이더 생성 오류를 발생시킵니다. 표현식은 반드시 const-expression이어야 합니다. 이 문장은 셰이더에서 정적 접근 조건을 만족할 수 있지만, 그 외에는 컴파일된 셰이더에 아무런 영향을 주지 않습니다. Const 단언문은 모듈 범위 또는 함수 범위 문장으로 나타날 수 있습니다.

assert_statement :

const_assert

예시: 정적 단언문 예시
const x = 1;
const y = 2;
const_assert x < y; // 모듈 범위에서 유효함.
const_assert(y != 0); // 괄호는 선택사항임.

fn foo() {
  const z = x + y - 2;
  const_assert z > 0; // 함수 내에서 유효함.
  let a  = 3;
  const_assert a != 0; // 유효하지 않음, 표현식은 const-expression이어야 함.
}

11. 함수

함수는 호출 시 계산 작업을 수행합니다.

함수는 다음 방식 중 하나로 호출됩니다:

WGSL의 함수는 정의 순서에 제한이 없으며, 사용 후에 정의되어도 됩니다. 따라서 함수 원형 또는 전방 선언이 필요 없고, 할 수 있는 방법도 없습니다.

함수에는 두 가지 종류가 있습니다:

11.1. 사용자 정의 함수 선언

함수 선언사용자 정의 함수를 생성하며, 다음을 지정합니다:

함수 선언은 반드시 모듈 범위에서만 등장해야 합니다. 함수 이름은 프로그램 전체에서 스코프 내에 존재합니다.

참고:사용자 정의 함수는 오직 하나의 오버로드만 가집니다.

형식 매개변수 선언은 함수 호출 시 제공되어야 하는 값의 식별자 이름과 타입을 지정합니다. 형식 매개변수는 속성을 가질 수 있습니다. § 11.2 함수 호출 참고. 식별자의 스코프함수 본문입니다. 하나의 함수에 대해 두 형식 매개변수가 같은 이름을 가지면 안 됩니다.

참고: 일부 내장 함수는 추상 숫자 타입 매개변수를 허용할 수 있습니다. 하지만, 이 기능은 사용자 선언 함수에서는 현재 지원되지 않습니다.

반환 타입이 지정된 경우, 반드시 생성가능 타입이어야 합니다.

WGSL에서 함수 선언에 적용할 수 있는 속성은 다음과 같습니다:

WGSL에서 함수 매개변수와 반환 타입에 적용할 수 있는 속성은 다음과 같습니다:

function_decl :

attribute * function_header compound_statement

function_header :

'fn' ident '(' param_list ? ')' ( '->' attribute * template_elaborated_ident ) ?

param_list :

param ( ',' param ) * ',' ?

param :

attribute * ident ':' type_specifier

예시: 간단한 함수
// add_two 함수 선언.
// 매개변수 i와 b가 있음.
// 반환 타입은 i32.
// return 문장이 있는 본문을 가짐.
fn add_two(i: i32, b: f32) -> i32 {
  return i + 2;  // 매개변수는 본문에서 사용 가능.
}

// compute 셰이더 엔트리 포인트 함수 'main'.
// 반환 타입이 없음.
// add_two 함수를 호출하고,
// 결과값을 'six'에 저장함.
@compute @workgroup_size(1)
fn main() {
   let six: i32 = add_two(4, 5.0);
}

11.2. 함수 호출

함수 호출은 함수를 실행시키는 문장 또는 표현식입니다.

함수 호출이 포함된 함수는 호출 함수 또는 호출자라 합니다. 호출되는 함수는 피호출 함수 또는 피호출자라 합니다.

함수 호출은 다음을 포함합니다:

함수 호출은 반드시 피호출 함수의 형식 매개변수 개수와 동일한 개수의 인자를 제공해야 합니다. 각 인자 값은 반드시 해당 위치의 형식 매개변수와 동일한 타입으로 평가되어야 합니다.

함수 호출 시 요약:

  1. 호출 함수의 실행이 일시 중지됨.

  2. 피호출 함수return할 때까지 실행됨.

  3. 호출 함수의 실행이 재개됨.

피호출 함수가 return하는 방식:

함수 호출이 실행될 때 상세 절차:

  1. 함수 호출 인자 값 평가(평가 순서는 좌→우).

  2. 호출 함수의 실행이 중단됨. 모든 함수 범위 변수 및 상수는 현재 값을 유지.

  3. 피호출 함수가 사용자 정의 함수라면, 피호출 함수의 함수 범위 변수마다 메모리 할당.

  4. 피호출 함수의 형식 매개변수 값은 함수 호출 인자 값을 위치로 매칭하여 결정. 예: 첫 번째 형식 매개변수는 호출 위치의 첫 번째 인자 값을 가짐.

  5. 피호출 함수로 제어 이동. 피호출 함수가 사용자 정의 함수라면 본문의 첫 문장부터 실행.

  6. 피호출 함수가 return할 때까지 실행.

  7. 호출 함수로 제어가 돌아오고, 피호출 함수 실행이 중지 해제됨. 피호출 함수가 값을 반환하면 그 값이 함수 호출 표현식의 값이 됨.

함수 호출 위치는 호출 위치라 하며, call_phrase 문법 규칙의 파싱 인스턴스에서 첫 번째 토큰 위치를 의미합니다. 호출 위치는 동적 컨텍스트입니다. 따라서 동일한 텍스트 위치가 여러 호출 위치를 나타낼 수 있습니다.

참고: fragment 셰이더에서 모든 쿼드discard될 경우, 함수 호출이 return하지 않을 수 있습니다. 이런 경우 제어가 호출 함수로 돌아오지 않습니다.

11.3. const 함수

const 속성으로 선언된 함수는 셰이더 생성 시점에 평가될 수 있습니다. 이러한 함수는 const 함수라 부릅니다. 이 함수 호출은 const-expression의 일부가 될 수 있습니다.

함수에 const-expression이 아닌 표현식이나, const 선언이 아닌 선언이 포함되어 있으면 셰이더 생성 오류입니다.

참고: const 속성은 사용자 선언 함수에는 적용할 수 없습니다.

예시: const 함수
const first_one = firstLeadingBit(1234 + 4567); // 12로 평가됨
                                                // first_one의 타입은 i32,
                                                // firstLeadingBit는 AbstractInt에 작동하지 않으므로

@id(1) override x : i32;
override y = firstLeadingBit(x); // const-expression은 override-expression에서도 사용 가능
                                 // firstLeadingBit(x)는 이 문맥에서 const-expression이 아님

fn foo() {
  var a : array<i32, firstLeadingBit(257)>; // const 함수는 모든 인자가 const-expression이면 const-expression에서 사용 가능
}

11.4. 함수의 제한 사항

참고: 재귀는 모든 선언 종류에 대해 순환이 허용되지 않으므로 사용할 수 없습니다.

예시: 유효 및 무효 포인터 인자
fn bar(p : ptr<function, f32>) {
}

fn baz(p : ptr<private, i32>) {
}

fn bar2(p : ptr<function, f32>) {
  let a = &*&*(p);

  bar(p); // 유효함
  bar(a); // 유효함
}

fn baz2(p : ptr<storage, f32>) {
}

struct S {
  x : i32
}

@group(0) @binding(0)
var<storage> ro_storage : f32;
@group(0) @binding(1)
var<storage, read_write> rw_storage : f32;

var usable_priv : i32;
var unusable_priv : array<i32, 4>;
fn foo() {
  var usable_func : f32;
  var unusable_func : S;
  var i32_func : i32;

  let a_priv = &usable_priv;
  let b_priv = a_priv;
  let c_priv = &*&usable_priv;
  let d_priv = &(unusable_priv.x);
  let e_priv = d_priv;

  let a_func = &usable_func;
  let b_func = &unusable_func;
  let c_func = &(*b_func)[0];
  let d_func = c_func;
  let e_func = &*a_func;

  baz(&usable_priv); // 유효함, 변수의 address-of.
  baz(a_priv);       // 유효함, 실질적으로 변수의 address-of.
  baz(b_priv);       // 유효함, 실질적으로 변수의 address-of.
  baz(c_priv);       // 유효함, 실질적으로 변수의 address-of.
  baz(d_priv);       // 유효함, 메모리 뷰가 변경됨.
  baz(e_priv);       // 유효함, 메모리 뷰가 변경됨.
  baz(&i32_func);    // 무효, 주소 공간 불일치.

  bar(&usable_func); // 유효함, 변수의 address-of.
  bar(c_func);       // 유효함, 메모리 뷰가 변경됨.
  bar(d_func);       // 유효함, 메모리 뷰가 변경됨.
  bar(e_func);       // 유효함, 실질적으로 변수의 address-of.

  baz2(&ro_storage); // 유효함, 변수의 address-of.
  baz2(&rw_storage); // 무효, 접근 모드 불일치.
}

11.4.1. 별칭 분석

11.4.1.1. 루트 식별자

메모리 위치는 함수 실행 중 메모리 뷰를 통해 접근할 수 있습니다. 함수 내에서 각 메모리 뷰는 특정 루트 식별자를 가지며, 이는 해당 함수에서 처음 그 메모리에 접근하는 변수 또는 형식 매개변수의 이름을 의미합니다.

로컬에서 유도된 reference 또는 pointer 타입의 표현식은 특정 루트 식별자에 대해 새로운 이름을 도입할 수 있지만, 각 표현식은 정적으로 결정 가능한 루트 식별자를 가집니다.

pointer 또는 reference type인 표현식 E에 대해, 루트 식별자는 다음과 같이 찾은 원본 변수 또는 형식 매개변수 입니다:

11.4.1.2. 별칭(Aliasing)

루트 식별자원본 변수는 함수의 호출 위치에 따라 달라지는 동적 개념이지만, WGSL 모듈은 각 루트 식별자에 대해 가능한 모든 원본 변수 집합을 정적으로 분석할 수 있습니다.

루트 식별자가 같은 원본 변수를 가지면 별칭(alias) 관계입니다. WGSL 함수의 실행은 절대 별칭된 루트 식별자를 통해 한쪽에서 쓰기, 다른 쪽에서 읽기 또는 쓰기가 동시에 발생하는 메모리 접근을 하면 안 됩니다. 이 조건은 호출 그래프의 잎(leaf)부터 위로(즉, 위상 순서) 프로그램을 분석하여 판정합니다. 각 함수에 대해 분석은 다음 집합을 기록합니다:

함수의 각 호출 위치에서 다음 중 하나가 발생하면 셰이더 생성 오류입니다:

예시: 별칭 분석
var<private> x : i32 = 0;

fn f1(p1 : ptr<function, i32>, p2 : ptr<function, i32>) {
  *p1 = *p2;
}

fn f2(p1 : ptr<function, i32>, p2 : ptr<function, i32>) {
  f1(p1, p2);
}

fn f3() {
  var a : i32 = 0;
  f2(&a, &a);  // 잘못됨. 두 개의 포인터 매개변수를 전달할 수 없음
               // 하나 이상의 루트 식별자가 동일하고, 그 중 하나 이상이
               // (서브 함수에 의해) 쓰여질 때.
}

fn f4(p1 : ptr<function, i32>, p2 : ptr<function, i32>) -> i32 {
  return *p1 + *p2;
}

fn f5() {
  var a : i32 = 0;
  let b = f4(&a, &a); // 올바름. f4에서 p1과 p2는 모두 읽기만 함.
}

fn f6(p : ptr<private, i32>) {
  x = *p;
}

fn f7(p : ptr<private, i32>) -> i32 {
  return x + *p;
}

fn f8() {
  let a = f6(&x); // 잘못됨. x는 전역 변수로 쓰이고
                  // 매개변수로 읽힘.
  let b = f7(&x); // 올바름. x는 매개변수와 변수로 모두 읽기만 함.
}

12. 속성(Attribute)

속성(attribute)은 객체를 수정합니다. WGSL은 속성을 적용하기 위한 통합 문법을 제공합니다. 속성은 API와의 인터페이스 지정 등 다양한 목적으로 사용됩니다.

일반적으로 언어 관점에서 속성은 타입 및 의미론 검사 목적에서는 무시될 수 있습니다. 또한 속성 이름은 컨텍스트 의존 이름이며, 일부 속성 파라미터도 컨텍스트 의존 이름입니다.

attribute :

'@' ident_pattern_token argument_expression_list ?

| align_attr

| binding_attr

| blend_src_attr

| builtin_attr

| const_attr

| diagnostic_attr

| group_attr

| id_attr

| interpolate_attr

| invariant_attr

| location_attr

| must_use_attr

| size_attr

| workgroup_size_attr

| vertex_attr

| fragment_attr

| compute_attr

속성 설명에서 명시적으로 허용하지 않는 한, 하나의 객체 또는 타입에 대해 동일 속성을 두 번 이상 지정하면 안 됩니다.

12.1. align

align_attr :

'@' 'align' '(' expression ',' ? ')'

align 속성
설명 구조체 멤버의 메모리 배치를 제약합니다.

반드시 구조체 타입의 멤버에만 적용해야 합니다.

이 속성은 해당 구조체 타입 값이 메모리에서 어떻게 나타날 수 있는지에 영향을 미치며, 구조체 자체와 그 구성 멤버가 나타날 수 있는 바이트 주소를 제약합니다.

align(n)이 타입 TS 멤버에 적용되고, S가 주소 공간 AS의 변수에 대한 저장 타입이 될 수 있을 때, ASuniform이 아니라면, n은 다음을 만족해야 합니다:
n = k × RequiredAlignOf(T,AS) 여기서 k는 양의 정수입니다.

정렬 및 크기 규칙은 상호 재귀적입니다. 하지만 위 제약은 중첩 타입의 필수 정렬에만 의존하므로 잘 정의됩니다. 타입의 중첩 깊이는 유한합니다.

§ 14.4 메모리 레이아웃 참고.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다.
반드시 양수여야 합니다.
반드시 2의 거듭제곱이어야 합니다.

12.2. binding

binding_attr :

'@' 'binding' '(' expression ',' ? ')'

binding 속성
설명 바인드 group에서 리소스의 바인딩 번호를 지정합니다. § 13.3.2 리소스 인터페이스 참고.

반드시 리소스 변수에만 적용해야 합니다.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다.
반드시 0 이상이어야 합니다.

12.3. blend_src

blend_src_attr :

'@' 'blend_src' '(' expression ',' ? ')'

blend_src 속성
설명 dual_source_blending 기능이 활성화된 경우 fragment 출력의 일부를 지정합니다. § 13.3.1.3 입출력 위치 참고.

반드시 location 속성이 있는 구조체 타입의 멤버에만 적용해야 합니다. 반드시 숫자 스칼라 또는 숫자 벡터 타입 객체 선언에만 적용해야 합니다. 포함되면 안 됨 셰이더 단계 입력에. 포함되면 안 됨 셰이더 단계 출력에, 단 fragment 셰이더 단계는 예외.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32이고 값이 0 또는 1이어야 합니다.

12.4. builtin

builtin_attr :

'@' 'builtin' '(' builtin_value_name ',' ? '')

builtin 속성
설명 연결된 객체가 지정된 토큰으로 표시되는 내장 값임을 지정합니다. § 13.3.1.1 내장 입력 및 출력 참고.

반드시 엔트리 포인트 함수의 매개변수, 엔트리 포인트 반환 타입 또는 구조체 멤버에만 적용해야 합니다.

파라미터 반드시 내장 값 이름-토큰이어야 하며, 내장 값이어야 합니다.

12.5. const

const_attr :

'@' 'const'

const 속성
설명 해당 함수가 const 함수로 사용될 수 있음을 지정합니다. 이 속성은 사용자 정의 함수에는 적용할 수 없습니다.

반드시 함수 선언에만 적용해야 합니다.

참고: 이 속성은 어떤 내장 함수가 const-expression에서 사용될 수 있는지 표기 관례로 사용됩니다.

파라미터 없음

12.6. diagnostic

diagnostic_attr :

'@' 'diagnostic' diagnostic_control

diagnostic_control :

'(' severity_control_name ',' diagnostic_rule_name ',' ? ')

diagnostic 속성
설명 범위 진단 필터를 지정합니다. § 2.3 진단 참고.

하나의 구문 형태에 둘 이상의 diagnostic 속성을 지정할 수 있지만, 반드시 서로 다른 트리거 규칙을 지정해야 합니다.

파라미터 첫 번째 파라미터는 severity_control_name입니다.

두 번째 파라미터는 diagnostic_rule_name 토큰으로, 트리거 규칙을 지정합니다.

12.7. group

group_attr :

'@' 'group' '(' expression ',' ? ')

group 속성
설명 리소스의 바인딩 그룹을 지정합니다. § 13.3.2 리소스 인터페이스 참고.

반드시 리소스 변수에만 적용해야 합니다.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다.
반드시 0 이상이어야 합니다.

12.8. id

id_attr :

'@' 'id' '(' expression ',' ? ')

id 속성
설명 파이프라인 오버라이더블 상수에 대한 대체 이름으로 숫자 식별자를 지정합니다.

반드시 override 선언스칼라 타입에만 적용해야 합니다.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다.
반드시 0 이상이어야 합니다.

12.9. interpolate

interpolate_attr :

'@' 'interpolate' '(' interpolate_type_name ',' ? ')'

| '@' 'interpolate' '(' interpolate_type_name ',' interpolate_sampling_name ',' ? ')'

interpolate_type_name :

ident_pattern_token

interpolate 속성
설명 사용자 정의 IO가 어떻게 반드시 보간되어야 하는지 지정합니다. 이 속성은 사용자 정의 vertex 출력과 fragment 입력에서만 의미가 있습니다. § 13.3.1.4 보간 참고.

반드시 location 속성이 적용된 선언에만 적용해야 합니다.

파라미터 첫 번째 파라미터는 반드시 보간 타입 이름-토큰이어야 하며 보간 타입이어야 합니다.

두 번째 파라미터가 있으면 반드시 보간 샘플링 이름-토큰이어야 하며, 보간 샘플링이어야 합니다.

12.10. invariant

invariant_attr :

'@' 'invariant'

invariant 속성
설명 vertex 셰이더의 position 내장 출력 값에 적용될 때, 결과 계산이 서로 다른 프로그램과 동일 엔트리 포인트의 여러 호출에서 불변임을 의미합니다. 즉, 두 position 출력이 서로 다른 엔트리 포인트에서 데이터와 제어 흐름이 일치하면, 결과값도 동일함을 보장합니다. position 내장 입력 값에는 아무런 영향이 없습니다.

반드시 position 내장 값에만 적용해야 합니다.

참고: 이 속성은 HLSL의 precise 한정자와 GLSL의 invariant 한정자에 대응됩니다.

파라미터 없음

12.11. location

location_attr :

'@' 'location' '(' expression ',' ? ')'

location 속성
설명 엔트리 포인트의 사용자 정의 IO의 일부를 지정합니다. § 13.3.1.3 입출력 위치 참고.

반드시 엔트리 포인트 함수 매개변수, 엔트리 포인트 반환 타입, 또는 구조체 타입의 멤버에만 적용해야 합니다. 반드시 숫자 스칼라 또는 숫자 벡터 타입 객체 선언에만 적용해야 합니다. 포함되면 안 됨 compute 셰이더 단계 입력에.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다.
반드시 0 이상이어야 합니다.

12.12. must_use

must_use_attr :

'@' 'must_use'

must_use 속성
설명 이 함수에 대한 호출표현식으로 반드시 사용되어야 함을 지정합니다. 즉, 이 함수 호출이 함수 호출 문장 전체가 되어서는 안 됩니다.

반드시 function 선언에만 적용되어야 하며, return type이 있어야 합니다.

참고: 많은 함수는 값을 반환하고 부작용이 없습니다. 그러한 함수를 함수 호출 문장의 전부로 호출하는 것은 흔히 프로그래밍 결함입니다. 이런 특성을 가진 내장 함수는 @must_use로 선언됩니다. 사용자 정의 함수도 @must_use 속성을 가질 수 있습니다.

참고: @must_use 규칙을 의도적으로 우회하려면, 가짜 대입문을 사용하거나 값 선언을 통해 함수 호출을 초기값으로 지정하세요.

파라미터 없음

12.13. size

size_attr :

'@' 'size' '(' expression ',' ? ')'

size 속성
설명 구조체 멤버에 예약된 바이트 수를 지정합니다.

이 값은 반드시 해당 멤버 타입의 바이트 크기 이상이어야 합니다:

size(n)이 타입 T의 멤버에 적용되면, SizeOf(T) ≤ n을 만족해야 합니다.

§ 14.4 메모리 레이아웃 참고.

반드시 구조체 타입의 멤버에만 적용해야 하며, 멤버 타입은 creation-fixed footprint를 반드시 가져야 합니다.

파라미터 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다.
반드시 양수여야 합니다.

12.14. workgroup_size

workgroup_size_attr :

'@' 'workgroup_size' '(' expression ',' ? ')'

| '@' 'workgroup_size' '(' expression ',' expression ',' ? ')'

| '@' 'workgroup_size' '(' expression ',' expression ',' expression ',' ? ')'

workgroup_size 속성
설명 컴퓨트 셰이더의 워크그룹 그리드의 x, y, z 차원을 지정합니다.

첫 번째 파라미터는 x 차원을 지정합니다. 두 번째 파라미터가 제공되면 y 차원을 지정하며, 제공되지 않으면 1로 간주합니다. 세 번째 파라미터가 제공되면 z 차원을 지정하며, 제공되지 않으면 1로 간주합니다.

반드시 컴퓨트 셰이더 진입점 함수에만 적용되어야 합니다. 다른 객체에는 적용되어서는 안 됩니다.

파라미터 하나, 둘, 또는 셋의 파라미터를 받습니다.

각 파라미터는 반드시 const-expression 또는 override-expression이어야 하며, 모든 파라미터는 반드시 동일 타입이어야 하며, i32 또는 u32여야 합니다.

셰이더 생성 오류는 지정된 파라미터가 const-expression이고 그 값이 0 이하일 경우 발생합니다.

파이프라인 생성 오류는 지정된 파라미터가 0 이하이거나 WebGPU API가 지정한 상한을 초과하거나, 모든 파라미터 곱이 WebGPU API가 지정한 상한을 초과할 경우 발생합니다 (WebGPU § 3.6.2 한계 참고).

12.15. 셰이더 스테이지 속성

아래 셰이더 스테이지 속성들은 함수를 특정 셰이더 스테이지엔트리 포인트로 지정합니다. 이러한 속성들은 함수 선언에만 적용되어야 하며, 한 함수에는 최대 하나만 존재할 수 있습니다. 이 속성들은 매개변수를 받지 않습니다.

12.15.1. vertex

vertex_attr :

'@' 'vertex'

vertex 속성은 해당 함수를 버텍스 셰이더 스테이지엔트리 포인트로 선언하며, 렌더 파이프라인에 사용됩니다.

12.15.2. fragment

fragment_attr :

'@' 'fragment'

fragment 속성은 해당 함수를 프래그먼트 셰이더 스테이지엔트리 포인트로 선언하며, 렌더 파이프라인에 사용됩니다.

12.15.3. compute

compute_attr :

'@' 'compute'

compute 속성은 해당 함수를 컴퓨트 셰이더 스테이지엔트리 포인트로 선언하며, 컴퓨트 파이프라인에 사용됩니다.

13. 엔트리 포인트

엔트리 포인트는 특정 셰이더 스테이지의 작업을 수행하는 사용자 정의 함수입니다.

13.1. 셰이더 스테이지

WebGPU는 draw 또는 dispatch 명령의 형태로 GPU에 작업을 전달합니다. 이러한 명령들은 셰이더 스테이지의 입력, 출력 및 연결된 리소스들의 컨텍스트에서 파이프라인을 실행합니다.

파이프라인은 GPU에서 수행할 작업을 정의하며, 일부가 프로그래머블한 여러 단계의 순서로 이루어집니다. WebGPU에서는 실행할 draw 또는 dispatch 명령을 예약하기 전에 파이프라인을 생성합니다. 파이프라인에는 두 가지 종류가 있습니다: GPUComputePipeline과 GPURenderPipeline.

dispatch 명령GPUComputePipeline을 사용하여 컴퓨트 셰이더 스테이지를 논리적 포인트 그리드에 대해 실행하며, 병렬 처리의 양을 제어할 수 있고, 버퍼 및 이미지 리소스를 읽거나 갱신할 수 있습니다.

draw 명령GPURenderPipeline을 사용하여 두 개의 프로그래머블 스테이지와 기타 고정 기능 스테이지로 이루어진 다단계 프로세스를 실행합니다:

WebGPU 명세는 파이프라인에 대해 더 자세하게 설명합니다.

WGSL은 파이프라인의 프로그래머블 부분에 해당하는 세 가지 셰이더 스테이지를 정의합니다:

각 셰이더 스테이지는 고유의 특성과 제약을 가지며, 자세한 내용은 다른 부분에서 설명합니다.

13.2. 엔트리 포인트 선언

엔트리 포인트를 만들려면, 셰이더 스테이지 속성이 달린 사용자 정의 함수를 선언하세요.

WebGPU API에서 파이프라인을 구성할 때, 엔트리 포인트의 함수 이름은 WebGPU GPUProgrammableStage 객체의 entryPoint 속성에 매핑됩니다.

엔트리 포인트의 형식 매개변수들은 해당 스테이지의 셰이더 스테이지 입력을 나타냅니다. 엔트리 포인트의 반환값(명시된 경우)은 해당 스테이지의 셰이더 스테이지 출력을 나타냅니다.

각 형식 매개변수의 타입과 엔트리 포인트의 반환 타입은 다음 중 하나여야 합니다:

구조체 타입은 사용자 정의 입력내장 입력을 함께 그룹화할 때 사용할 수 있습니다. 구조체 타입은 반환 타입으로 사용자 정의 출력내장 출력을 함께 그룹화할 때 사용할 수 있습니다.

참고: bool 타입은 사용자 정의 입력 및 출력에는 사용할 수 없습니다. front_facing 내장 값에만 허용됩니다.

참고: Compute 엔트리 포인트는 반환 타입을 가질 수 없습니다.

예시: 엔트리 포인트
@vertex
fn vert_main() -> @builtin(position) vec4<f32> {
  return vec4<f32>(0.0, 0.0, 0.0, 1.0);
}

@fragment
fn frag_main(@builtin(position) coord_in: vec4<f32>) -> @location(0) vec4<f32> {
  return vec4<f32>(coord_in.x, coord_in.y, 0.0, 1.0);
}

@compute @workgroup_size(1)
fn comp_main() { }

셰이더 스테이지의 함수 집합은 다음을 합집합한 것입니다:

합집합은 반복적으로 적용되어 안정화될 때까지 계속됩니다. 유한한 단계 내에 안정화됩니다.

13.2.1. 엔트리 포인트 함수 속성

WGSL은 엔트리 포인트 선언에 적용할 수 있는 다음 속성들을 정의합니다:

예시: workgroup_size 속성
@compute @workgroup_size(8,4,1)
fn sorter() { }

@compute @workgroup_size(8u)
fn reverser() { }

// 파이프라인 오버라이드 가능한 상수 사용 예시
@id(42) override block_width = 12u;
@compute @workgroup_size(block_width)
fn shuffler() { }

// 오류: workgroup_size는 컴퓨트 셰이더에 지정되어야 함
@compute
fn bad_shader() { }

13.3. 셰이더 인터페이스

셰이더 인터페이스는 셰이더가 셰이더 단계 외부의 데이터를 읽거나 쓸 수 있도록 접근하는 객체 집합과, 셰이더를 구성하기 위해 사용되는 파이프라인-오버라이더블 상수를 포함한다. 인터페이스에는 다음이 포함된다:

선언 D는 셰이더에 의해 정적으로 접근됨으로 간주될 때:

참고:정적 접근은 다음을 고려하여 재귀적으로 정의됩니다:

이제 셰이더의 인터페이스를 다음으로 정확히 정의할 수 있다:

13.3.1. 단계 간 입력 및 출력 인터페이스

셰이더 단계 입력은 파이프라인 상류에서 셰이더 단계에 제공되는 데이터다. 각 데이터는 내장 입력 값 또는 사용자 정의 입력 중 하나다.

셰이더 단계 출력은 셰이더가 파이프라인 하류의 추가 처리에 제공하는 데이터다. 각 데이터는 내장 출력 값 또는 사용자 정의 출력 중 하나다.

IO 어트리뷰트는 객체를 셰이더 단계 입력 또는 셰이더 단계 출력으로 지정하거나, 입력 또는 출력의 특성을 추가로 설명하는 데 사용된다. IO 어트리뷰트 종류는 다음과 같다:

13.3.1.1. 내장 입력 및 출력

내장 입력 값은 시스템에서 생성된 제어 정보를 접근하기 위한 것이다. 엔트리 포인트 반드시 중복된 내장 입력을 포함하지 않아야 한다.

단계 S의 이름 X 및 타입 TX을 가진 내장 입력은 셰이더 단계 S에 대한 엔트리 포인트형식 매개변수를 통해 접근되며, 두 가지 방식이 있다:

  1. 매개변수에 builtin(X) 어트리뷰트가 있고 타입이 TX이다.

  2. 매개변수에 구조체 타입이 선언되어 있고, 구조체 멤버 중 하나가 builtin(X) 어트리뷰트와 TX 타입을 가진다.

반대로 엔트리 포인트의 매개변수 또는 매개변수 멤버에 builtin 어트리뷰트가 있으면, 해당 builtin 반드시 해당 셰이더 단계의 입력이어야 한다.

내장 출력 값은 셰이더가 파이프라인의 이후 처리 단계에 제어 정보를 전달하는 데 사용된다. 엔트리 포인트 반드시 중복된 내장 출력을 포함하지 않아야 한다.

단계 S에 대해 이름 Y 및 타입 TY을 가진 내장 출력은 셰이더 단계 S엔트리 포인트반환값을 통해 다음 두 가지 방식으로 설정된다:

  1. 엔트리 포인트 반환 타입builtin(Y) 어트리뷰트가 있고 타입이 TY이다.

  2. 엔트리 포인트 반환 타입이 구조체 타입이고, 구조체 멤버 중 하나가 builtin(Y) 어트리뷰트와 TY 타입을 가진다.

반대로 엔트리 포인트의 반환 타입 또는 반환 타입의 멤버에 builtin 어트리뷰트가 있으면, 해당 builtin 반드시 해당 셰이더 단계의 출력이어야 한다.

참고: position 내장은 버텍스 셰이더의 출력이자 프래그먼트 셰이더의 입력이다.

내장 입력 값과 내장 출력 값을 통틀어 내장 값이라 한다.

아래 표는 사용 가능한 내장 값을 요약한다. 각각은 내장 값 이름 토큰이며, 내장 값에 해당한다. 각각은 이후 섹션에서 자세히 설명된다.

내장 입력 및 출력 값
이름 단계 방향 타입 확장
vertex_index 버텍스 입력 u32
instance_index 버텍스 입력 u32
clip_distances 버텍스 출력 array<f32, N> (N8) clip_distances
position 버텍스 출력 vec4<f32>
프래그먼트 입력 vec4<f32>
front_facing 프래그먼트 입력 bool
frag_depth 프래그먼트 출력 f32
primitive_index 프래그먼트 입력 u32 primitive_index
sample_index 프래그먼트 입력 u32
sample_mask 프래그먼트 입력 u32
프래그먼트 출력 u32
local_invocation_id 컴퓨트 입력 vec3<u32>
local_invocation_index 컴퓨트 입력 u32
global_invocation_id 컴퓨트 입력 vec3<u32>
workgroup_id 컴퓨트 입력 vec3<u32>
num_workgroups 컴퓨트 입력 vec3<u32>
subgroup_invocation_id 컴퓨트 입력 u32 subgroups
프래그먼트
subgroup_size 컴퓨트 입력 u32 subgroups
프래그먼트
예시: 내장 값 선언
 struct VertexOutput {
   @builtin(position) my_pos: vec4<f32>,
   @builtin(clip_distances) my_clip_distances: array<f32, 8>,
 }

 @vertex
 fn vs_main(
   @builtin(vertex_index) my_index: u32,
   @builtin(instance_index) my_inst_index: u32,
 ) -> VertexOutput {}

 struct FragmentOutput {
   @builtin(frag_depth) depth: f32,
   @builtin(sample_mask) mask_out: u32
 }

 @fragment
 fn fs_main(
   @builtin(front_facing) is_front: bool,
   @builtin(position) coord: vec4<f32>,
   @builtin(sample_index) my_sample_index: u32,
   @builtin(sample_mask) mask_in: u32,
 ) -> FragmentOutput {}

 @compute @workgroup_size(64)
 fn cs_main(
   @builtin(local_invocation_id) local_id: vec3<u32>,
   @builtin(local_invocation_index) local_index: u32,
   @builtin(global_invocation_id) global_id: vec3<u32>,
) {}
13.3.1.1.1. clip_distances
이름 clip_distances
단계 vertex
타입 array<f32, N>
방향 Output
설명 배열의 각 값은 사용자 정의 클립 평면과의 거리를 나타냅니다. 0의 클립 거리 값은 꼭짓점(vertex)이 평면 위에 있다는 의미이고, 양수 값은 꼭짓점이 클립 하프스페이스(clip half-space) 안에 있음을, 음수 값은 꼭짓점이 클립 하프스페이스 밖에 있음을 의미합니다. clip_distances 배열의 크기는 반드시8이어야 합니다. 자세한 내용은 WebGPU § 23.2.4 원시 클리핑(Primitive Clipping)을 참고하세요.
13.3.1.1.2. frag_depth
이름 frag_depth
단계 fragment
타입 f32
방향 Output
설명 프래그먼트(fragment)의 뷰포트 깊이 범위 내에서 갱신된 깊이 값입니다.

자세한 내용은 WebGPU § 3.3 좌표계(Coordinate Systems)를 참고하세요.

13.3.1.1.3. front_facing
이름 front_facing
단계 fragment
타입 bool
방향 Input
설명 현재 프래그먼트가 전면(Front-facing) 기본 도형(primitive)에 해당하면 True이고, 그렇지 않으면 False입니다.
13.3.1.1.4. global_invocation_id
이름 global_invocation_id
단계 compute
타입 vec3<u32>
방향 Input
설명 현재 호출(invocation)의 글로벌 호출 ID(global invocation ID)로, 컴퓨트 셰이더 그리드 내의 위치입니다. global_invocation_id 값은 workgroup_id * workgroup_size + local_invocation_id와 같습니다.
13.3.1.1.5. instance_index
이름 instance_index
단계 vertex
타입 u32
방향 Input
설명 현재 API 레벨 draw 명령 내에서 현재 꼭짓점의 인스턴스 인덱스입니다.

첫 번째 인스턴스의 인덱스는 draw의 firstInstance 인자 값과 같으며, 직접적으로 또는 간접적으로 제공됩니다. draw의 추가 인스턴스마다 인덱스는 1씩 증가합니다.

13.3.1.1.6. local_invocation_id
이름 local_invocation_id
단계 compute
타입 vec3<u32>
방향 Input
설명 현재 호출(invocation)의 로컬 호출 ID(local invocation ID)로, 워크그룹 그리드(workgroup grid) 내의 위치입니다.
13.3.1.1.7. local_invocation_index
이름 local_invocation_index
단계 compute
타입 u32
방향 Input
설명 현재 호출(invocation)의 로컬 호출 인덱스(local invocation index)로, 워크그룹 그리드 내 위치의 선형화된 인덱스입니다.
13.3.1.1.8. num_workgroups
이름 num_workgroups
단계 compute
타입 vec3<u32>
방향 Input
설명 디스패치 크기(dispatch size)로, vec3<u32>(group_count_x, group_count_y, group_count_z) 형태로 컴퓨트 셰이더가 API에 의해 디스패치(dispatched) 될 때 사용됩니다.
13.3.1.1.9. position
이름 position
단계 vertex
타입 vec4<f32>
방향 Output
설명 현재 꼭짓점의 클립 위치(clip position)로, 클립 공간 좌표(clip space coordinates)로 표현됩니다.

출력 값 (x,y,z,w)은 다음과 같이 WebGPU 정규화 장치 좌표(normalized device coordinates)에서 (x/w, y/w, z/w)로 매핑됩니다.

자세한 내용은 WebGPU § 3.3 좌표계(Coordinate Systems)WebGPU § 23.2.4 원시 클리핑(Primitive Clipping)을 참고하세요.

w 좌표가 0이면 동적 오류(dynamic error)가 발생합니다.

이름 position
단계 fragment
타입 vec4<f32>
방향 Input
설명
현재 프래그먼트의 입력 위치입니다.

fp를 프래그먼트의 입력 위치라고 합시다.
rp를 프래그먼트의 래스터화 지점(RasterizationPoint)이라 하고,
vp를 draw 명령에 적용되는 [[viewport]]라 합시다.

그러면 개략적으로:

fp.xy = rp.destination.position
fp.z = rp.depth
fp.w = rp.perspectiveDivisor

자세히 보면:

  • fp.x와 fp.y는 현재 프래그먼트의 위치에 대한 보간된 x, y 좌표입니다. 프레임버퍼(framebuffer) 내에서의 위치입니다.

    프레임버퍼는 좌상단이 (0.0,0.0), 우하단이 (vp.width, vp.height)인 2차원 픽셀 그리드입니다. 각 픽셀은 x, y 방향 모두 1.0 단위의 크기를 가지며, 픽셀의 중심은 정수 좌표로부터 (0.5,0.5) 만큼 오프셋되어 있습니다.

  • fp.z는 현재 프래그먼트의 보간된 깊이 값입니다. 예:

    • 정규화 장치 좌표(normalized device coordinates)에서 깊이 0은 fp.z = vp.minDepth에 매핑됩니다.

    • 깊이 1은 fp.z = vp.maxDepth에 매핑됩니다.

  • fp.w는 프래그먼트의 원근 분모(perspective divisor)로, position 버텍스 셰이더 출력의 w 성분을 vertex_w라 할 때, 1.0 ÷ vertex_w를 보간한 값입니다.

자세한 내용은 WebGPU § 3.3 좌표계(Coordinate Systems)WebGPU § 23.2.5 래스터화(Rasterization)를 참고하세요.

13.3.1.1.10. primitive_index
이름 primitive_index
단계 fragment
타입 u32
방향 Input
설명 현재 인스턴스에서 드로우 작업이 시작된 이후 처리된 프리미티브 수에 기반한 프리미티브 단위 인덱스입니다. 0부터 시작하여 각 포인트, 라인, 트라이앵글 프리미티브가 처리될 때마다 1씩 증가합니다. 인스턴스가 그려질 때마다 0으로 리셋됩니다. 프리미티브 리스타트 값(primitive restart value)으로 스트립 프리미티브를 다시 시작해도 프리미티브 인덱스에는 영향을 주지 않습니다. 인덱스 값은 해당 프리미티브의 모든 프래그먼트에서 동일합니다.
13.3.1.1.11. sample_index
이름 sample_index
단계 fragment
타입 u32
방향 Input
설명 현재 프래그먼트의 샘플 인덱스입니다. 값은 최소 0이고 최대 sampleCount-1이며, sampleCount는 GPU 렌더 파이프라인에 지정된 MSAA 샘플 count입니다. 이 어트리뷰트가 적용될 때, sample_index 값에 따라 프래그먼트 셰이더 효과가 달라진다면, fragment 셰이더는 각 샘플마다 한 번씩 호출됩니다.

자세한 내용은 WebGPU § 10.3 GPURenderPipeline을 참고하세요.

13.3.1.1.12. sample_mask
이름 sample_mask
단계 fragment
타입 u32
방향 Input
설명 현재 프래그먼트의 샘플 커버리지 마스크입니다. 이 비트마스크는 해당 프래그먼트에서 프리미티브가 커버하는 샘플들을 나타냅니다.

자세한 내용은 WebGPU § 23.2.11 샘플 마스킹(Sample Masking)을 참고하세요.

이름 sample_mask
단계 fragment
타입 u32
방향 Output
설명 현재 프래그먼트의 샘플 커버리지 마스크 제어 값입니다. 이 변수에 마지막으로 기록된 값이 셰이더 출력 마스크(shader-output mask)가 됩니다. 기록된 값의 비트가 0이면 해당 색상 어태치먼트의 샘플이 버려집니다.

자세한 내용은 WebGPU § 23.2.11 샘플 마스킹(Sample Masking)을 참고하세요.

13.3.1.1.13. vertex_index
이름 vertex_index
단계 vertex
타입 u32
방향 Input
설명 현재 API 레벨 드로우 명령에서, 인스턴싱과 관계없이 현재 꼭짓점의 인덱스입니다.

비인덱스 드로우의 경우, 첫 꼭짓점 인덱스는 드로우의 firstVertex 인자 값과 같으며, 직접 또는 간접적으로 제공됩니다. 드로우 인스턴스의 추가 꼭짓점마다 인덱스는 1씩 증가합니다.

인덱스 드로우의 경우, 인덱스는 꼭짓점의 인덱스 버퍼 값에 드로우의 baseVertex 인자 값을 더한 값입니다. 직접 또는 간접적으로 제공됩니다.

13.3.1.1.14. workgroup_id
이름 workgroup_id
단계 compute
타입 vec3<u32>
방향 Input
설명 현재 호출(invocation)의 워크그룹 ID(workgroup ID)로, 전체 컴퓨트 셰이더 그리드(compute shader grid) 내에서의 워크그룹 위치입니다.

같은 워크그룹 내 모든 호출은 동일한 워크그룹 ID를 갖습니다.

워크그룹 ID는 (0,0,0)에서 (group_count_x - 1, group_count_y - 1, group_count_z - 1)까지의 범위를 가집니다.

13.3.1.1.15. subgroup_invocation_id
이름 subgroup_invocation_id
단계 compute 또는 fragment
타입 u32
방향 Input
설명 현재 호출(invocation)의 서브그룹 호출 ID(subgroup invocation ID)입니다.

ID는 [0, subgroup_size - 1] 범위 내에 있습니다.

13.3.1.1.16. subgroup_size
이름 subgroup_size
단계 compute 또는 fragment
타입 u32
방향 Input
설명 현재 호출의 서브그룹 서브그룹 크기(subgroup size)입니다.
13.3.1.2. 사용자 정의 입력 및 출력

사용자 정의 데이터는 파이프라인의 시작점에 입력으로 전달되거나, 파이프라인 단계 사이에 전달되거나, 파이프라인의 끝에서 출력될 수 있습니다.

사용자 정의 입력 데이터(user-defined input datum)사용자 정의 출력 데이터(user-defined output datum)반드시 다음을 만족해야 합니다:

compute 셰이더는 사용자 정의 입력 또는 출력을 가질 수 없습니다.

13.3.1.3. 입출력 위치

각 입출력 위치는 최대 16바이트 크기의 값을 저장할 수 있습니다. 타입의 바이트 크기는 § 14.4.1 정렬과 크기SizeOf 열을 사용하여 정의됩니다. 예를 들어, 네 개의 부동소수점 성분을 가진 벡터는 하나의 위치를 차지합니다.

IO 위치는 location 어트리뷰트를 통해 지정됩니다.

각 사용자 정의 입력출력 반드시 명시적으로 지정된 IO 위치를 가져야 합니다. 엔트리 포인트 IO의 각 구조체 멤버는 반드시 내장 값(자세한 내용은 § 13.3.1.1 내장 입력 및 출력 참고) 중 하나이거나, 위치가 할당되어야 합니다.

WGSL 모듈에서 정의된 각 엔트리 포인트에 대해, inputs를 해당 셰이더 단계의 입력 집합(즉, 형식 매개변수 또는 구조체 타입의 형식 매개변수의 멤버의 위치)으로 합시다.
WGSL 모듈에서 정의된 각 구조체 타입 S(셰이더 단계 입력 또는 출력에 사용되는 경우뿐만 아니라)에 대해, memberslocation 어트리뷰트가 지정된 S의 멤버 집합으로 합시다.

참고: 위치 번호는 입력과 출력에서 구분됩니다: 엔트리 포인트 셰이더 단계의 입력에 대한 위치 번호는 엔트리 포인트 셰이더 단계의 출력에 대한 위치 번호와 충돌하지 않습니다.

참고: 엔트리 포인트의 출력에서 위치가 겹치는 것을 방지하기 위한 추가 규칙은 필요하지 않습니다. 출력이 구조체일 경우 위 첫 번째 규칙이 겹침을 방지합니다. 그렇지 않으면 출력은 스칼라 또는 벡터이고, 하나의 위치만 할당될 수 있습니다.

참고: 엔트리 포인트에서 사용할 수 있는 위치의 수는 WebGPU API에 의해 정의됩니다.

예시: location 어트리뷰트 적용
struct A {
  @location(0) x: f32,
  // 위치가 16바이트임에도 불구하고, x와 y는 같은 위치를 공유할 수 없습니다
  @location(1) y: f32
}

// in1은 위치 0과 1을 차지합니다.
// in2는 위치 2를 차지합니다.
// 반환값은 위치 0을 차지합니다.
@fragment
fn fragShader(in1: A, @location(2) in2: f32) -> @location(0) vec4<f32> {
 // ...
}

사용자 정의 IO는 같은 구조체 내에서 내장 값과 혼합할 수 있습니다. 예:

예시: 내장값과 사용자 정의 IO 혼합
// 내장값과 사용자 정의 입력 혼합 예시.
struct MyInputs {
  @location(0) x: vec4<f32>,
  @builtin(front_facing) y: bool,
  @location(1) @interpolate(flat) z: u32
}

struct MyOutputs {
  @builtin(frag_depth) x: f32,
  @location(0) y: vec4<f32>
}

@fragment
fn fragShader(in1: MyInputs) -> MyOutputs {
  // ...
}
예시: 잘못된 location 할당
struct A {
  @location(0) x: f32,
  // 잘못됨, x와 y는 같은 위치를 공유할 수 없습니다.
  @location(0) y: f32
}

struct B {
  @location(0) x: f32
}

struct C {
  // 잘못됨, 사용자 정의 IO가 있는 구조체는 중첩될 수 없습니다.
  b: B
}

struct D {
  x: vec4<f32>
}

@fragment
// 잘못됨, 구조체 타입에 location을 적용할 수 없습니다.
fn fragShader1(@location(0) in1: D) {
  // ...
}

@fragment
// 잘못됨, in1과 in2는 같은 위치를 공유할 수 없습니다.
fn fragShader2(@location(0) in1: f32, @location(0) in2: f32) {
  // ...
}

@fragment
// 잘못됨, 구조체에 location을 적용할 수 없습니다.
fn fragShader3(@location(0) in1: vec4<f32>) -> @location(0) D {
  // ...
}
13.3.1.4. 보간(interpolation)

저자는 interpolate 어트리뷰트를 사용하여 사용자 정의 IO 데이터의 보간 방식을 제어할 수 있습니다. WGSL은 보간의 두 가지 측면, 즉 보간 타입과 샘플링을 제어할 수 있도록 제공합니다.

보간 타입(interpolation type)반드시 다음 중 하나의 보간 타입 이름 토큰이어야 합니다:

perspective

값이 원근 보정 방식으로 보간됩니다.

linear

값이 선형, 즉 원근 보정 없는 방식으로 보간됩니다.

flat

값이 보간되지 않습니다.

보간 샘플링(interpolation sampling)반드시 다음 중 하나의 보간 샘플링 이름 토큰이어야 합니다:

center

보간은 픽셀의 중심에서 수행됩니다.

centroid

보간은 프래그먼트가 현재 프리미티브 내에서 커버하는 모든 샘플이 포함된 지점에서 수행됩니다. 이 값은 프리미티브의 모든 샘플에서 동일합니다.

sample

보간이 각 샘플마다 수행됩니다. 이 어트리뷰트가 적용될 때 fragment 셰이더가 각 샘플마다 한 번씩 호출됩니다.

first

값이 프리미티브의 첫 번째 꼭짓점에서 제공됩니다.

either

값이 프리미티브의 첫 번째 꼭짓점 또는 마지막 꼭짓점에서 제공됩니다. 값이 첫 번째 또는 마지막 꼭짓점에서 오는지는 구현에 따라 다릅니다.

스칼라 또는 벡터 부동소수점 타입의 사용자 정의 IO의 경우:

스칼라 또는 벡터 정수 타입의 사용자 정의 vertex 출력 및 fragment 입력은 반드시 항상 flat 보간 타입으로 지정되어야 합니다.

단계 간 인터페이스 검증에서는 렌더 파이프라인 내에서 각 사용자 정의 fragment 입력의 보간 속성이 동일한 location 할당을 가진 vertex 출력의 보간 속성과 일치하는지 확인합니다. 그렇지 않으면 파이프라인 생성 오류발생합니다.

13.3.2. 리소스 인터페이스

리소스(resource)셰이더 단계 외부의 데이터에 접근할 수 있도록 하는 객체로, override 선언이나 셰이더 단계 입력/출력이 아닙니다. 리소스는 셰이더의 모든 호출(invocation)에서 공유됩니다.

리소스에는 네 가지 종류가 있습니다:

셰이더의 리소스 인터페이스(resource interface of a shader)셰이더 단계의 함수에서 정적으로 접근되는 모듈 범위의 리소스 변수 집합입니다.

각 리소스 변수는 반드시 groupbinding 어트리뷰트와 함께 선언되어야 합니다. 셰이더의 단계와 함께 이 값들은 셰이더 파이프라인에서 리소스의 바인딩 주소를 식별합니다. 자세한 내용은 WebGPU § 8.3 GPUPipelineLayout을 참고하세요.

같은 셰이더 내의 두 개의 서로 다른 리소스 변수는 반드시 groupbinding 값이 쌍으로 같아서는 안 됩니다.

13.3.3. 리소스 레이아웃 호환성

WebGPU는 셰이더의 리소스 인터페이스가 셰이더를 사용하는 파이프라인의 레이아웃과 일치해야 합니다.

WGSL 변수의 리소스 인터페이스가 WebGPU binding member 또는 binding type과 호환되지 않는 경우, 이는 파이프라인 생성 오류입니다. 호환성은 아래 표에서 정의됩니다.

WebGPU 바인딩 타입 호환성
WGSL 리소스 WebGPU binding member WebGPU binding type
uniform buffer buffer GPUBufferBindingType "uniform"
storage buffer read_write 접근 권한 "storage"
storage buffer read 접근 권한 "read-only-storage"
sampler sampler GPUSamplerBindingType "filtering"
"non-filtering"
sampler_comparison "comparison"
sampled texture, depth texture, 또는 multisampled texture texture GPUTextureSampleType "float"
"unfilterable-float"
"sint"
"uint"
"depth"
write-only storage texture storageTexture GPUStorageTextureAccess "write-only"
read-write storage texture "read-write"
read-only storage texture "read-only"
external texture externalTexture (해당 없음)

인터페이스 검증 요구사항은 WebGPU API 명세를 참고하세요.

13.3.4. 버퍼 바인딩이 런타임 크기 배열의 요소 개수를 결정함

storage buffer 변수에 런타임 크기 배열이 포함되어 있으면, 해당 배열의 요소 개수는 해당 resource의 크기로 결정됩니다.

자세히 설명하면, NRuntime은 타입 RAT의 런타임 크기 배열에 대해 다음과 같이 계산됩니다:

truncate((EBBS − array_offset) ÷ array_stride), 여기서:

셰이더는 NRuntimearrayLength 내장 함수로 계산할 수 있습니다.

참고: 이 알고리즘은 모호성이 없습니다: 런타임 크기 배열이 더 큰 타입의 일부일 때, 반드시 구조체의 마지막 요소로만 올 수 있으며, 구조체 자체는 또 다른 배열이나 구조체의 일부가 될 수 없습니다.

NRuntime은 해당 버퍼 바인딩의 크기에 따라 결정되며, draw 또는 dispatch command마다 달라질 수 있습니다.

WebGPU 검증 규칙은 1 ≤ NRuntime임을 보장합니다.

다음 코드 샘플에서:
예시: 단순 런타임 크기 배열의 요소 개수
@group(0) @binding(1) var<storage> weights: array<f32>;

아래 표는 weights 변수에 대해, 대응하는 effective buffer binding size별 NRuntime 예시를 보여줍니다.

단순 런타임 크기 배열의 요소 개수 예시
Effective buffer binding size weights 변수의 NRuntime 계산식
1024 256 truncate( 1024 ÷ 4 )
1025 256 truncate( 1025 ÷ 4 )
1026 256 truncate( 1026 ÷ 4 )
1027 256 truncate( 1027 ÷ 4 )
1028 257 truncate( 1028 ÷ 4 )
다음 코드 샘플에서:
예시: 복잡한 런타임 크기 배열의 요소 개수
struct PointLight {                          //             align(16) size(32)
  position : vec3f,                          // offset(0)   align(16) size(12)
  // -- implicit member alignment padding -- // offset(12)            size(4)
  color : vec3f,                             // offset(16)  align(16) size(12)
  // -- implicit struct size padding --      // offset(28)            size(4)
}

struct LightStorage {                        //             align(16)
  pointCount : u32,                          // offset(0)   align(4)  size(4)
  // -- implicit member alignment padding -- // offset(4)             size(12)
  point : array<PointLight>,                 // offset(16)  align(16) elementsize(32)
}

@group(0) @binding(1) var<storage> lights : LightStorage;

아래 표는 lights 변수의 point 멤버에 대해 NRuntime 예시를 보여줍니다.

복잡한 런타임 크기 배열의 요소 개수 예시
Effective buffer binding size lights 변수의 point 멤버의 NRuntime 계산식
1024 31 truncate( ( 1024 - 16 ) ÷ 32) )
1025 31 truncate( ( 1025 - 16 ) ÷ 32) )
1039 31 truncate( ( 1039 - 16 ) ÷ 32) )
1040 32 truncate( ( 1040 - 16 ) ÷ 32) )

14. 메모리

WGSL에서 저장 가능한(storable) 타입의 값은 메모리에 저장되어 나중에 다시 가져올 수 있습니다. 본 섹션에서는 메모리의 구조와, 메모리에 접근하는 연산의 의미론을 설명합니다. 메모리에 저장될 수 있는 값의 타입과, 메모리 접근에 사용되는 타입에 대해서는 § 6.4 메모리 뷰를 참조하세요.

14.1. 메모리 위치

메모리는 각각 구분되는 메모리 위치(memory location)들의 집합으로 구성됩니다. 각 메모리 위치는 8비트 크기를 가집니다. 메모리에 영향을 주는 연산은 하나 이상의 메모리 위치 집합에 작용합니다. 합성 타입(composite)에 대한 메모리 연산은 패딩(padding) 메모리 위치에 접근하지 않습니다. 따라서 연산에 의해 접근되는 메모리 위치 집합은 연속적일 필요가 없습니다.

두 메모리 위치 집합은 오버랩(overlap)된다고 하며, 이는 두 집합의 교집합이 비어있지 않을 때입니다.

14.2. 메모리 접근 모드

메모리 접근(memory access)이란 메모리 위치에 작용하는 연산을 말합니다.

하나의 연산은 읽기, 쓰기, 또는 읽기와 쓰기를 모두 수행할 수 있습니다.

특정 메모리 위치는 메모리의 접근 모드(access mode)로 표시되는, 특정 종류의 접근만을 지원할 수 있습니다.

접근 모드
접근 모드 지원되는 접근 방식
read 읽기 접근만 지원하며, 쓰기는 불가합니다.
write 쓰기 접근만 지원하며, 읽기는 불가합니다.
read_write 읽기와 쓰기 접근 모두 지원합니다.

WGSL은 선언(predeclare)열거자(enumerant) read, write, read_write를 제공합니다.

14.3. 주소 공간(address space)

메모리 위치는 주소 공간(address spaces)으로 분할됩니다. 각 주소 공간은 고유한 특성을 가지며, 변경 가능성, 가시성, 포함 가능한 값, 그리고 변수 사용 방법을 결정합니다. 자세한 내용은 § 7 변수 및 값 선언을 참조하세요.

특정 메모리 뷰의 접근 모드는 보통 문맥에 따라 결정됩니다:

storage 주소 공간은 읽기(read)읽기/쓰기(read_write) 접근 모드를 모두 지원합니다. 나머지 주소 공간들은 한 가지 접근 모드만 지원합니다. 각 주소 공간의 기본 접근 모드는 다음 표에 설명되어 있습니다.

주소 공간
주소 공간 호출(invocation) 간 공유 기본 접근 모드 비고
function 동일 호출만 공유 read_write
private 동일 호출만 공유 read_write
workgroup 동일 컴퓨트 셰이더(compute shader) 워크그룹(workgroup) 내 호출 간 공유 read_write 최상위 배열의 요소 개수(element count)파이프라인 오버라이더블(pipeline-overridable) 상수일 수 있습니다.
uniform 동일 셰이더 스테이지(shader stage) 내 호출 간 공유 read uniform buffer 변수에 해당
storage 동일 셰이더 스테이지 내 호출 간 공유 read storage buffer 변수에 해당
handle 동일 셰이더 스테이지 내 호출 간 공유 read 샘플러(sampler)텍스처(texture) 변수에 해당

WGSL은 선언(predeclare)된 각 주소 공간에 대해 열거자(enumerant)를 제공하지만, handle 주소 공간은 예외입니다.

workgroup 주소 공간의 변수반드시 정적 접근(statically accessed)컴퓨트 셰이더 스테이지에서 해야 합니다.

storage 주소 공간(storage buffer)의 변수는 버텍스 셰이더 스테이지(vertex shader stage)에서 읽기 접근 모드일 때에만 정적으로 접근할 수 있습니다. store typestorage texture이고 쓰기(write) 또는 읽기/쓰기(read_write) 접근 모드인 변수는 버텍스 셰이더 스테이지에서 정적으로 접근할 수 없습니다. WebGPU createBindGroupLayout()를 참조하세요.

참고: 각 주소 공간은 성능 특성이 다를 수 있습니다.

WGSL 소스에서 변수 선언이나 포인터 타입을 작성할 때:

14.4. 메모리 레이아웃

WGSL에서 타입의 레이아웃은 주소 공간과는 독립적입니다. 하지만 엄밀히 말하면, 해당 레이아웃은 호스트-공유(host-shareable) 버퍼에서만 관찰될 수 있습니다. Uniform bufferstorage buffer 변수는 메모리 내 바이트 시퀀스로 구성된 대량 데이터를 공유하는 데 사용됩니다. 버퍼는 CPU와 GPU 사이, 또는 서로 다른 셰이더 스테이지 간, 또는 다른 파이프라인 간에 공유될 수 있습니다.

버퍼 데이터는 재포맷이나 변환 없이 공유되므로, 버퍼 생성자와 소비자가 메모리 레이아웃(버퍼 내 바이트가 WGSL 타입 값으로 어떻게 조직되는지에 대한 설명)에 동의하지 않는다면 동적 오류가 발생합니다. 이러한 바이트는 공통 기준 위치(base location)에 상대적인 값의 메모리 위치입니다.

버퍼 변수의 store type반드시 호스트-공유 타입이어야 하며, 아래에 설명된 대로 완전히 명시된 메모리 레이아웃을 가져야 합니다.

각 버퍼 변수는 반드시 uniform 또는 storage 주소 공간에 선언되어야 합니다.

타입의 메모리 레이아웃은 다음과 같은 경우에만 의미가 있습니다:

8비트 바이트는 호스트-공유 메모리의 가장 기본 단위입니다. 본 섹션에서 정의된 용어들은 8비트 바이트 수를 표현합니다.

다음과 같은 표기법을 사용합니다. T호스트-공유 또는 고정 풋프린트(fixed footprint) 타입, S는 호스트-공유 또는 고정 풋프린트 구조체 타입, A는 호스트-공유 또는 고정 풋프린트 배열 혹은 런타임 크기 배열입니다:

14.4.1. 정렬값과 크기

호스트-공유 또는 고정 풋프린트 데이터 타입 T는 정렬값과 크기를 가집니다.

타입의 정렬값(alignment)은 해당 타입 값이 메모리 어디에 배치될 수 있는지를 제한하는 정수입니다: 타입의 정렬값은 반드시 값 시작 메모리 위치의 바이트 주소를 나누어 떨어져야 합니다. 정렬값은 값에 더 효율적인 하드웨어 명령어를 사용하거나, 특정 주소 공간에서 더 엄격한 하드웨어 요구 사항을 만족시키기 위해 존재합니다. (주소 공간 레이아웃 제약 참고)

참고: 각 정렬값은 항상 2의 거듭제곱입니다.

타입 또는 구조체 멤버의 바이트 크기(byte-size)란 해당 타입이나 구조체 멤버의 값을 저장하기 위해 호스트-공유 메모리에 예약된 연속 바이트 수입니다. 크기에는 타입 끝에 비주소화 패딩(padding)이 포함될 수 있습니다. 결과적으로, 값을 불러오거나 저장할 때 값의 크기보다 적은 메모리 위치에 접근할 수 있습니다.

호스트-공유고정 풋프린트 타입의 정렬값과 크기는 아래 표에서 재귀적으로 정의됩니다:

호스트-공유 및 고정 풋프린트 타입의 정렬값과 크기
호스트-공유 또는 고정 풋프린트 타입 T AlignOf(T) SizeOf(T)
bool
참고 참조.
4 4
i32, u32, 또는 f32 4 4
f16 2 2
atomic<T> 4 4
vec2<T>, Tbool, i32, u32, 또는 f32일 때 8 8
vec2<f16> 4 4
vec3<T>, Tbool, i32, u32, 또는 f32일 때 16 12
vec3<f16> 8 6
vec4<T>, Tbool, i32, u32, 또는 f32일 때 16 16
vec4<f16> 8 8
matCxR (열 우선, col-major)

(일반형)

AlignOf(vecR) SizeOf(array<vecR, C>)
mat2x2<f32> 8 16
mat2x2<f16> 4 8
mat3x2<f32> 8 24
mat3x2<f16> 4 12
mat4x2<f32> 8 32
mat4x2<f16> 4 16
mat2x3<f32> 16 32
mat2x3<f16> 8 16
mat3x3<f32> 16 48
mat3x3<f16> 8 24
mat4x3<f32> 16 64
mat4x3<f16> 8 32
mat2x4<f32> 16 32
mat2x4<f16> 8 16
mat3x4<f32> 16 48
mat3x4<f16> 8 24
mat4x4<f32> 16 64
mat4x4<f16> 8 32
struct S (멤버 M1...MN) max(AlignOfMember(S,1), ... , AlignOfMember(S,N))
roundUp(AlignOf(S), justPastLastMember)

여기서 justPastLastMember = OffsetOfMember(S,N) + SizeOfMember(S,N)
array<E, N>
AlignOf(E) N × roundUp(AlignOf(E), SizeOf(E))
array<E>
AlignOf(E) NRuntime × roundUp(AlignOf(E),SizeOf(E))

여기서 NRuntime은 T의 런타임 결정 요소 개수입니다.
참고: 많은 GPU는 단일 바이트 쓰기를 데이터 레이스 없이 구현할 수 없습니다. bool 값이 4바이트 크기와 4바이트 정렬값을 갖도록 명시함으로써, 구현체는 인접한 boolean 값들을 데이터 레이스 없이 메모리에 저장할 수 있습니다.

14.4.2. 구조체 멤버 레이아웃

구조체의 내부 레이아웃은 멤버들의 크기와 정렬값으로 계산됩니다. 기본적으로, 멤버들은 겹침 없이, 순서대로, 멤버 정렬 요구사항을 만족하면서 촘촘하게 배치됩니다.

이 기본 내부 레이아웃은 레이아웃 속성(layout attributes)에 의해 오버라이드할 수 있습니다. 레이아웃 속성은 다음과 같습니다:

구조체 타입 Si번째 멤버는 SizeOfMember(S, i)와 AlignOfMember(S, i)로 표시되는 크기와 정렬값을 가집니다. 멤버 크기와 정렬값은 각 멤버가 구조체 시작점으로부터 떨어진 바이트 오프셋을 계산하는 데 사용됩니다. 자세한 내용은 § 14.4.4 값의 내부 레이아웃에 나와 있습니다.

SizeOfMember(S, i)는 Si번째 멤버에 size(k) 속성이 있으면 k입니다. 그렇지 않으면 해당 멤버의 타입 T에 대해 SizeOf(T)입니다.
AlignOfMember(S, i)는 Si번째 멤버에 align(k) 속성이 있으면 k입니다. 그렇지 않으면 해당 멤버 타입 T에 대해 AlignOf(T)입니다.

구조체 멤버에 size 속성이 지정된 경우, 값은 해당 멤버 타입의 크기 이상이어야 합니다:

SizeOfMember(S, i) ≥ SizeOf(T)
여기서 TSi번째 멤버의 타입입니다.

첫 번째 구조체 멤버는 항상 구조체 시작점에서 바이트 오프셋 0을 가집니다:

OffsetOfMember(S, 1) = 0

그 이후 멤버는 해당 멤버 타입의 정렬 요구조건을 만족하면서, 이전 멤버와 겹치지 않는 가장 낮은 오프셋에 배치됩니다. 각 멤버 인덱스 i > 1에 대해:

OffsetOfMember(S, i) = roundUp(AlignOfMember(S, i ), OffsetOfMember(S, i-1) + SizeOfMember(S, i-1))
예시: 암시적 멤버 크기와 정렬값을 사용하는 구조체 레이아웃
struct A {                                     //             align(8)  size(24)
    u: f32,                                    // offset(0)   align(4)  size(4)
    v: f32,                                    // offset(4)   align(4)  size(4)
    w: vec2<f32>,                              // offset(8)   align(8)  size(8)
    x: f32                                     // offset(16)  align(4)  size(4)
    // -- implicit struct size padding --      // offset(20)            size(4)
}

struct B {                                     //             align(16) size(160)
    a: vec2<f32>,                              // offset(0)   align(8)  size(8)
    // -- implicit member alignment padding -- // offset(8)             size(8)
    b: vec3<f32>,                              // offset(16)  align(16) size(12)
    c: f32,                                    // offset(28)  align(4)  size(4)
    d: f32,                                    // offset(32)  align(4)  size(4)
    // -- implicit member alignment padding -- // offset(36)            size(4)
    e: A,                                      // offset(40)  align(8)  size(24)
    f: vec3<f32>,                              // offset(64)  align(16) size(12)
    // -- implicit member alignment padding -- // offset(76)            size(4)
    g: array<A, 3>,    // element stride 24       offset(80)  align(8)  size(72)
    h: i32                                     // offset(152) align(4)  size(4)
    // -- implicit struct size padding --      // offset(156)           size(4)
}

@group(0) @binding(0)
var<storage,read_write> storage_buffer: B;
예시: 명시적 멤버 크기와 정렬값을 사용하는 구조체 레이아웃
struct A {                                     //             align(8)  size(32)
    u: f32,                                    // offset(0)   align(4)  size(4)
    v: f32,                                    // offset(4)   align(4)  size(4)
    w: vec2<f32>,                              // offset(8)   align(8)  size(8)
    @size(16) x: f32                           // offset(16)  align(4)  size(16)
}

struct B {                                     //             align(16) size(208)
    a: vec2<f32>,                              // offset(0)   align(8)  size(8)
    // -- implicit member alignment padding -- // offset(8)             size(8)
    b: vec3<f32>,                              // offset(16)  align(16) size(12)
    c: f32,                                    // offset(28)  align(4)  size(4)
    d: f32,                                    // offset(32)  align(4)  size(4)
    // -- implicit member alignment padding -- // offset(36)            size(12)
    @align(16) e: A,                           // offset(48)  align(16) size(32)
    f: vec3<f32>,                              // offset(80)  align(16) size(12)
    // -- implicit member alignment padding -- // offset(92)            size(4)
    g: array<A, 3>,    // element stride 32       offset(96)  align(8)  size(96)
    h: i32                                     // offset(192) align(4)  size(4)
    // -- implicit struct size padding --      // offset(196)           size(12)
}

@group(0) @binding(0)
var<uniform> uniform_buffer: B;

14.4.3. 배열 레이아웃 예시

예시: 고정 크기 배열 레이아웃 예시
// 배열의 경우:
//   - 정렬값은 4 = AlignOf(f32)
//   - 요소 stride는 4 = roundUp(AlignOf(f32),SizeOf(f32)) = roundUp(4,4)
//   - 전체 크기는 32 = stride * 요소 개수 = 4 * 8
var small_stride: array<f32, 8>;

// 배열의 경우:
//   - 정렬값은 16 = AlignOf(vec3<f32>) = 16
//   - 요소 stride는 16 = roundUp(AlignOf(vec3<f32>), SizeOf(vec3<f32>))
//                          = roundUp(16,12)
//   - 전체 크기는 128 = stride * 요소 개수 = 16 * 8
var bigger_stride: array<vec3<f32>, 8>;
예시: 런타임 크기 배열 레이아웃 예시
// 배열의 경우:
//   - 정렬값은 4 = AlignOf(f32)
//   - 요소 stride는 4 = roundUp(AlignOf(f32),SizeOf(f32)) = 4
// B가 draw 또는 dispatch 명령에서 바인딩된 effective buffer binding size라면,
// 배열 요소 개수는:
//   N_runtime = floor(B / 요소 stride) = floor(B / 4)
@group(0) @binding(0)
var<storage> weights: array<f32>;

// 배열의 경우:
//   - 정렬값은 16 = AlignOf(vec3<f32>) = 16
//   - 요소 stride는 16 = roundUp(AlignOf(vec3<f32>), SizeOf(vec3<f32>))
//                          = roundUp(16,12)
// B가 draw 또는 dispatch 명령에서 바인딩된 effective buffer binding size라면,
// 배열 요소 개수는:
//   N_runtime = floor(B / 요소 stride) = floor(B / 16)
var<storage> directions: array<vec3<f32>>;

14.4.4. 값의 내부 레이아웃

이 섹션은 호스트-공유 값의 내부가 버퍼의 바이트 위치에 어떻게 배치되는지 설명합니다. 전체 값의 배치가 주어졌을 때, 이러한 레이아웃은 값의 타입과 구조체 멤버의 alignsize 속성에 따라 달라집니다.

값이 배치되는 버퍼 바이트 오프셋은 타입의 정렬 요구조건을 만족해야 합니다: 타입 T의 값이 버퍼 오프셋 k에 배치된다면, k = c × AlignOf(T) (단, c는 0 이상의 정수)이어야 합니다.

데이터는 주소 공간에 상관없이 동일하게 나타납니다.

참고: bool 타입은 호스트-공유가 아닙니다. WGSL은 bool 값이 4바이트 크기와 정렬값을 갖도록 명시하지만, 내부 레이아웃은 명시하지 않습니다.

u32 또는 i32 타입의 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:

참고: i32는 2의 보수 표현을 사용하므로 부호 비트는 31번 위치에 있습니다.

64비트 정수 레이아웃: WebGPU API의 일부 기능은 버퍼에 64비트 부호 없는 정수 값을 기록합니다. 이러한 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:

참고: WGSL에는 구체적인(concrete) 64비트 정수 타입이 없습니다.

f32 타입 값 VIEEE-754 binary32 포맷으로 표현됩니다. 1비트 부호, 8비트 지수, 23비트 분수(fraction)로 구성됩니다. V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:

f16 타입 값 VIEEE-754 binary16 포맷으로 표현됩니다. 1비트 부호, 5비트 지수, 10비트 분수로 구성됩니다. V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:

참고: 위 규칙은 숫자 값이 호스트-공유 버퍼에 리틀 엔디안(little-endian) 형식으로 저장됨을 의미합니다.

atomic 타입 atomic<T> 값 V가 호스트-공유 버퍼에 배치될 때, 내부 레이아웃은 기반 타입 T 값과 동일합니다.

벡터 타입 vecN<T> 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치될 때:

행렬 타입 matCxR<T> 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치될 때:

배열 타입 A 값이 호스트-공유 메모리 버퍼의 바이트 오프셋 k에 배치될 때:

구조체 타입 S의 값이 호스트-공유 메모리 버퍼의 바이트 오프셋 k에 배치될 때:

14.4.5. 주소 공간 레이아웃 제약

storageuniform 주소 공간은 서로 다른 버퍼 레이아웃 제약을 가지며, 이 절에서 설명합니다.

모든 주소 공간(address space)uniform을 제외하고 storage 주소 공간과 동일한 제약을 갖습니다.

변수가 직접 또는 간접적으로 참조하는 모든 구조체 및 배열 타입은 해당 변수의 주소 공간의 제약을 반드시 따라야 합니다. 주소 공간 제약을 위반하면 셰이더 생성 오류가 발생합니다.

이 절에서는 RequiredAlignOf(S, C)를 주소 공간 C에서 호스트-공유 또는 고정 풋프린트 타입 S 값의 바이트 오프셋 정렬값 요구사항으로 정의합니다.

주소 공간 C에서 호스트-공유 또는 고정 풋프린트 타입의 정렬값 요구사항
주소 공간 C에 나타날 수 있다고 가정한 호스트-공유 또는 고정 풋프린트 타입 S RequiredAlignOf(S, C),
Cuniform이 아닌 경우
RequiredAlignOf(S, C),
Cuniform인 경우
bool, i32, u32, f32, 또는 f16 AlignOf(S) AlignOf(S)
atomic<T> AlignOf(S) AlignOf(S)
vecN<T> AlignOf(S) AlignOf(S)
matCxR<T> AlignOf(S) AlignOf(S)
array<T, N> AlignOf(S) roundUp(16, AlignOf(S))
array<T> AlignOf(S) 해당 없음
struct S AlignOf(S) roundUp(16, AlignOf(S))

주소 공간 C에서 타입 T 구조체 멤버는 구조체 시작점으로부터 RequiredAlignOf(T, C)의 배수인 바이트 오프셋을 반드시 가져야 합니다:

OffsetOfMember(S, i) = k × RequiredAlignOf(T, C)
여기서 k는 0 이상의 정수이며, 구조체 Si번째 멤버의 타입이 T

주소 공간 C에서 요소 타입 T 배열은 RequiredAlignOf(T, C)의 배수인 요소 stride반드시 가져야 합니다:

StrideOf(array<T, N>) = k × RequiredAlignOf(T, C)
StrideOf(array<T>) = k × RequiredAlignOf(T, C)
여기서 k는 양의 정수임

uniform 주소 공간은 다음도 요구합니다:

참고: 아래 예시는 구조체 멤버에 alignsize 속성을 사용하여 uniform 버퍼의 레이아웃 요구사항을 만족시키는 방법을 보여줍니다. 특히 이러한 기법은 std140 레이아웃의 GLSL 버퍼를 WGSL로 기계적으로 변환할 때 사용할 수 있습니다.

예시: uniform 주소 공간의 오프셋 요구사항 만족
struct S {
  x: f32
}
struct Invalid {
  a: S,
  b: f32 // 잘못됨: a와 b 사이 오프셋이 4바이트이나, 최소 16이어야 함
}
@group(0) @binding(0) var<uniform> invalid: Invalid;

struct Valid {
  a: S,
  @align(16) b: f32 // 올바름: a와 b 사이 오프셋이 16바이트임
}
@group(0) @binding(1) var<uniform> valid: Valid;
예시: uniform 주소 공간의 stride 요구사항 만족
struct small_stride {
  a: array<f32,8> // stride 4
}
// 잘못됨, stride는 16의 배수여야 함
@group(0) @binding(0) var<uniform> invalid: small_stride;

struct wrapped_f32 {
  @size(16) elem: f32
}
struct big_stride {
  a: array<wrapped_f32,8> // stride 16
}
@group(0) @binding(1) var<uniform> valid: big_stride;     // 올바름

14.5. 메모리 모델

일반적으로 WGSL은 Vulkan 메모리 모델을 따릅니다. 이하 절에서는 WGSL 프로그램이 Vulkan 메모리 모델에 어떻게 매핑되는지 설명합니다.

참고: Vulkan 메모리 모델은 형식적 Alloy 모델의 텍스트 버전입니다.

14.5.1. 메모리 연산

WGSL에서 읽기 접근은 Vulkan 메모리 모델의 메모리 읽기 연산과 동일합니다. WGSL에서 쓰기 접근은 Vulkan 메모리 모델의 메모리 쓰기 연산과 동일합니다.

읽기 접근은 호출이 다음 중 하나를 실행할 때 발생합니다:

쓰기 접근은 호출이 다음 중 하나를 실행할 때 발생합니다:

Atomic read-modify-write 내장 함수는 읽기 접근쓰기 접근을 모두 수행하는 하나의 메모리 연산을 실행합니다.

읽기 및 쓰기 접근은 다른 경우에는 발생하지 않습니다. 읽기 및 쓰기 접근은 Vulkan 메모리 모델에서 메모리 연산(memory operation)이라 통칭합니다.

메모리 연산은 해당 연산에 사용된 메모리 뷰에 연결된 정확한 메모리 위치 집합만을 접근합니다. 예를 들어 구조체 여러 멤버 중 u32를 읽는 메모리 read는 해당 u32 멤버의 메모리 위치만 읽습니다.

참고: 벡터의 구성 요소에 쓰기 접근할 때 전체 벡터의 메모리 위치를 모두 접근할 수 있습니다.

예시: 메모리 위치 접근
struct S {
  a : f32,
  b : u32,
  c : f32
}

@group(0) @binding(0)
var<storage> v : S;

fn foo() {
  let x = v.b; // v.a나 v.c의 메모리 위치는 접근하지 않음.
}

14.5.2. 메모리 모델 참조

모듈 범위 resource 변수는 고유한 groupbinding 쌍에 대한 메모리 모델 참조(memory model reference)를 형성합니다. 그 외 변수(즉, function, private, workgroup 주소 공간)는 변수의 수명 동안 고유한 메모리 모델 참조를 형성합니다.

14.5.3. 스코프 연산

호출이 스코프 연산을 수행할 때, 한 세트 또는 두 세트의 호출에 영향을 줍니다. 이 집합들은 메모리 스코프와 실행 스코프입니다. 메모리 스코프는 해당 연산으로 메모리 내용이 변경될 때 이를 볼 수 있는 호출 집합을 지정합니다. 동기화 내장 함수에서는, 해당 함수 이전에 프로그램 순서대로 실행된 모든 메모리 연산이 함수 이후에 프로그램 순서대로 실행되는 연산에도 보이게 됨을 의미합니다. 실행 스코프는 연산에 참여할 수 있는 호출 집합을 지정합니다. (§ 15.6 Collective Operations 참고)

Atomic 내장 함수atomic 연산에 매핑되며, 해당 메모리 스코프(scope)는:

동기화 내장 함수는 실행 및 메모리 스코프Workgroup인 제어 배리어에 매핑됩니다.

암시적 및 명시적 derivatives는 암시적 쿼드(quad) 실행 스코프를 가집니다.

참고: 생성된 셰이더에서 Vulkan 메모리 모델이 활성화되지 않은 경우, QueueFamily 대신 Device 스코프를 사용해야 합니다.

14.5.4. 메모리 의미론

모든 Atomic 내장 함수Relaxed 메모리 의미론을 사용하며, 저장소 클래스 의미론은 적용되지 않습니다.

참고: WGSL에서 주소 공간은 SPIR-V의 저장소 클래스와 동일합니다.

workgroupBarrierAcquireRelease 메모리 의미론WorkgroupMemory 의미론을 사용합니다. storageBarrierAcquireRelease 메모리 의미론UniformMemory 의미론을 사용합니다. textureBarrierAcquireRelease 메모리 의미론ImageMemory 의미론을 사용합니다.

참고: workgroupBarrierstorageBarrier를 함께 사용하면 AcquireRelease 순서 의미론과 WorkgroupMemory, UniformMemory 메모리 의미론이 모두 적용됩니다.

참고: 어떤 atomic 또는 동기화 내장 함수도 MakeAvailable 또는 MakeVisible 의미론을 사용하지 않습니다.

14.5.5. 프라이빗 vs 논프라이빗

모든 비원자 읽기 접근storage 또는 workgroup 주소 공간에서 논프라이빗(non-private)으로 간주되며, NonPrivatePointer | MakePointerVisible 메모리 오퍼랜드와 Workgroup 스코프의 읽기 연산에 해당합니다.

모든 비원자 쓰기 접근storage 또는 workgroup 주소 공간에서 논프라이빗(non-private)으로 간주되며, NonPrivatePointer | MakePointerAvailable 메모리 오퍼랜드와 Workgroup 스코프의 쓰기 연산에 해당합니다.

모든 비원자 읽기 접근handle 주소 공간에서 논프라이빗(non-private)으로 간주되며, NonPrivateTexel | MakeTexelVisible 메모리 오퍼랜드와 Workgroup 스코프의 읽기 연산에 해당합니다.

모든 비원자 쓰기 접근handle 주소 공간에서 논프라이빗(non-private)으로 간주되며, NonPrivateTexel | MakeTexelAvailable 메모리 오퍼랜드와 Workgroup 스코프의 쓰기 연산에 해당합니다.

15. 실행

§ 1.1 개요에서는 셰이더가 어떻게 호출되고 호출(invocation)로 분할되는지 설명합니다. 본 섹션에서는 호출이 개별적 또는 집합적으로 어떻게 실행되는지에 대한 추가 제약을 설명합니다.

15.1. 호출 내 프로그램 순서

WGSL 모듈의 각 문장은 실행 중 0회 이상 실행될 수 있습니다. 특정 호출에 대해, 주어진 문장의 각 실행은 고유한 동적 문장 인스턴스가 됩니다.

문장에 표현식이 포함된 경우, 문장 의미론은 다음을 결정합니다:

표현식 중첩은 평가를 완료하기 위한 데이터 의존성을 정의합니다. 즉, 중첩된 표현식은 상위 표현식이 평가되기 전에 먼저 평가되어야 합니다. WGSL에서 표현식의 피연산자 평가 순서는 좌측에서 우측입니다. 예를 들어, foo() + bar()foo()를 먼저 평가하고 bar()를 그 다음에 평가해야 합니다. § 8 표현식을 참고하세요.

WGSL 모듈의 문장은 제어 흐름 순서대로 실행됩니다. § 9 문장§ 11.2 함수 호출을 참고하세요.

15.2. 균일성(Uniformity)

집합 연산(collective operation) (예: barrier, 도함수, 암묵적으로 계산된 도함수에 의존하는 텍스처 연산) 은 GPU에서 동시에 실행되는 여러 호출 간의 협력이 필요합니다. 모든 호출이 동시에, 즉 균일 제어 흐름(uniform control flow)에서 실행될 때 해당 연산은 올바르고 이식성 있게 실행됩니다.

반대로, 호출의 엄격한 부분집합만 해당 연산을 실행하는 경우(즉, 비균일 제어 흐름), 올바르지 않거나 이식성 없는 동작이 발생합니다. 비공식적으로, 일부 호출만 집합 연산에 도달하거나, 다른 호출은 그렇지 않거나, 비균일 제어 의존성의 결과로 동시에 도달하지 않습니다. 비균일 제어 의존성은 제어 흐름 문장이 비균일 값(non-uniform value)에 따라 동작할 때 발생합니다.

예를 들어, 서로 다른 호출이 if, break-if, while, for의 조건에 대해 서로 다른 값을 계산하거나, switch의 선택자 값을 다르게 계산하거나, 단락 평가 이항 연산자(&& 또는 ||)의 좌측 피연산자가 다를 때 비균일 제어 의존성이 발생합니다.

이러한 비균일 값은 종종 정적으로 균일하다고 증명되지 않은 특정 소스에서 유래할 수 있습니다. 대표적인 소스는 다음과 같습니다:

올바르고 이식 가능한 동작을 보장하기 위해, WGSL 구현은 정적 균일성 분석(uniformity analysis)을 수행하여 각 집합 연산이 균일 제어 흐름에서 실행된다는 것을 증명하려고 시도합니다. 이후의 하위 섹션에서 분석을 설명합니다.

균일성 실패(uniformity failure)발생하며, 균일성 분석이 특정 집합 연산균일 제어 흐름에서 실행된다는 것을 증명하지 못할 때 발생합니다.

15.2.1. 용어 및 개념

다음 정의는 단순히 직관을 제공하기 위한 것으로, 이후 하위 섹션에서 실제 분석이 계산하는 개념입니다. 분석이 실제로 이러한 개념과 프로그램의 유효성, 균일성 규칙 위반 여부를 정의합니다.

주어진 호출 집합에 대해:

15.2.2. 균일성 분석 개요

이후 하위 섹션에서는 집합 연산균일 제어 흐름에서만 실행되는지 확인하기 위한 정적 분석을 지정합니다.

분석은 동적 오류(dynamic error)가 발생하지 않는다고 가정합니다. 동적 오류가 발생한 셰이더 스테이지는 균일성 분석 결과와 관계 없이 이미 이식성이 없습니다.

참고:이 분석은 다음과 같은 바람직한 특성을 가집니다:

각 함수는 두 가지를 보장하도록 분석됩니다:

균일성 실패(uniformity failure)가 이 두 가지 중 하나라도 실패하면 트리거됩니다.

분석의 일환으로, 각 함수 호출에 대해 해당 호출이 균일 제어 흐름에서 실행된다고 증명할 수 없는 경우 트리거될 트리거 규칙(triggering rule) 집합을 계산하고 전파합니다. 이를 호출의 잠재적 트리거 집합(potential-trigger-set)이라고 합니다. 이 집합의 요소는 다음에서 선택됩니다:

15.2.3. 함수의 균일성 요구사항 분석

각 함수는 두 단계로 분석됩니다.

첫 번째 단계에서는 함수의 구문(syntax)을 따라가면서, 다음 하위 섹션의 규칙에 따라 방향성 그래프(directed graph)를 생성합니다. 두 번째 단계에서는 이 그래프를 탐색하여, 이 함수 호출에 대한 제약 조건을 계산하고, 균일성 실패(uniformity failure)가 발생할 수 있습니다.

참고: 네 개의 특수 노드 RequiredToBeUniform.error, RequiredToBeUniform.warning, RequiredToBeUniform.info, MayBeNonUniform을 제외하고, 각 노드는 다음 진술 중 하나의 진릿값을 나타낸다고 이해할 수 있습니다:

엣지(edge)는 출발 노드의 진술이 도착 노드의 진술을 암시(implication)한다고 이해할 수 있습니다.

예를 들어, 하나의 균일성 요구사항은 workgroupBarrier 내장 함수는 반드시 균일 제어 흐름에서만 호출되어야 한다는 것입니다. 이를 표현하려면 RequiredToBeUniform.error에서 workgroupBarrier 호출 위치(call site)에 해당하는 노드로 엣지를 추가합니다. RequiredToBeUniform.error는 명제 True에 해당하므로, RequiredToBeUniform.error -> X는 X가 참임을 의미합니다.

반대로, 어떤 것의 균일성을 보장할 수 없음을 표현하려면(예: thread id를 담는 변수), 해당 노드에서 MayBeNonUniform로 엣지를 추가합니다. MayBeNonUniform는 명제 False에 해당하므로, X -> MayBeNonUniform는 X가 거짓임을 의미합니다.

이 해석의 결과로, RequiredToBeUniform.error에서 도달 가능한 모든 노드는 프로그램이 유효하려면 반드시 균일해야 하는 것을 의미하고, MayBeNonUniform로 도달할 수 있는 모든 노드는 균일성을 보장할 수 없는 것을 의미합니다. 즉, RequiredToBeUniform.error에서 MayBeNonUniform까지 경로가 존재하면, 균일성 위반이 발생하여 균일성 실패(uniformity failure)가 트리거됩니다.

RequiredToBeUniform.warningRequiredToBeUniform.info 노드도 비슷하게 사용되지만, 경고(warning)정보(info) 진단(diagnostic)이 언제 트리거될지 판단하는 데 사용됩니다:

§ 2.3 진단(Diagnostics)에 설명된 바와 같이, 높은 심각도의 진단이 생성된 경우 낮은 심각도의 진단은 무시될 수 있습니다.

각 함수에 대해 두 개의 태그(tag)가 계산됩니다:

각 함수의 형식 파라미터(formal parameter)마다 하나 또는 두 개의 태그가 계산됩니다:

호출 위치 태그
Call Site Tag 설명
CallSiteRequiredToBeUniform.S,
여기서 Serror, warning, info 중 하나입니다.
해당 함수는 반드시 균일 제어 흐름(uniform control flow)에서만 호출되어야 합니다. 그렇지 않으면 S 심각도의 진단이 트리거됩니다.

잠재적 트리거 집합(potential-trigger-set)과 연결되어 있습니다.

CallSiteNoRestriction 함수는 비균일 제어 흐름(non-uniform control flow)에서 호출될 수 있습니다.
함수 태그
Function Tag 설명
ReturnValueMayBeNonUniform 함수의 반환 값이 비균일(non-uniform)할 수 있습니다.
NoRestriction 함수는 비균일성을 유발하지 않습니다.
파라미터 태그
Parameter Tag 설명
ParameterRequiredToBeUniform.S,
여기서 Serror, warning, info 중 하나입니다.
해당 파라미터는 반드시 균일 값(uniform value)이어야 합니다. 파라미터 타입이 포인터인 경우, 메모리 뷰 자체는 균일해야 하지만 반드시 그 내용까지 균일할 필요는 없습니다. 그렇지 않으면 S 심각도의 진단이 트리거됩니다.

잠재적 트리거 집합(potential-trigger-set)과 연결되어 있습니다.

ParameterContentsRequiredToBeUniform.S,
여기서 Serror, warning, info 중 하나입니다.
포인터 파라미터가 가리키는 메모리에 저장된 값은 반드시 균일 값이어야 합니다. 그렇지 않으면 S 심각도의 진단이 트리거됩니다.

잠재적 트리거 집합(potential-trigger-set)과 연결되어 있습니다.

ParameterNoRestriction 파라미터 값에 대한 균일성 요구사항이 없습니다.
파라미터 반환 태그
Parameter Return Tag 설명
ParameterReturnContentsRequiredToBeUniform 파라미터가 반드시 균일 값이어야 함수의 반환 값이 균일 값이 됩니다. 포인터 파라미터인 경우, 그 포인터가 가리키는 메모리의 값도 균일해야 합니다.
ParameterReturnNoRestriction 파라미터 값에 대한 균일성 요구사항이 없습니다.
포인터 파라미터 태그
Pointer Parameter Tag 설명
PointerParameterMayBeNonUniform 포인터 파라미터가 가리키는 메모리의 값이 함수 호출 후 비균일(non-uniform)해질 수 있습니다.
PointerParameterNoRestriction 포인터 파라미터가 가리키는 메모리 값의 균일성은 함수 호출에 의해 영향을 받지 않습니다.

다음 알고리즘은 주어진 함수에 대해 이러한 태그를 계산하는 방법을 설명합니다:

참고: 이 시점에서 전체 그래프는 파괴될 수 있다. 위에서 설명한 태그만 기억하면 이 함수의 호출자를 분석하는 데 충분하다. 그러나 그래프에는 더 많은 정보를 담고 있어, 더 자세한 진단 메시지를 제공할 수 있다. 예를 들어, 한 함수 내의 값이 균일(uniform)임을 증명할 수 없을 때, 이는 다른 함수에서 균일성 실패(uniformity failure)를 트리거하는 데 기여할 수 있다. 정보성 진단 메시지는 비균일 값뿐만 아니라 진단의 트리거 위치에서의 함수 호출까지 설명할 수 있다.

15.2.4. 포인터 디슈가링

function 주소 공간에 있는 포인터 타입의 각 파라미터는 해당 파라미터를 역참조(dereferencing)한 것과 동등한 초기 값을 갖는 로컬 변수 선언으로 디슈가링됩니다. 즉, function 주소 공간 포인터는 로컬 변수 선언의 별칭으로 간주됩니다. 초기 값 할당은 i번째 파라미터에 대한 param_i_contents로의 엣지를 생성합니다 (V(e)param_i_contents임).

let 선언 Leffective-value-type포인터 타입일 경우, 다음과 같이 디슈가링됩니다:

이 디슈가링은 포인터의 root identifier를 각 사용 지점에서 직접 노출함으로써 이후 분석을 단순화합니다.

참고: 균일성 분석 목적상, 타입 검사는 이 디슈가링 전후 모두 수행되는 것으로 간주합니다.

예시: 균일성 분석에서의 포인터
fn foo(p : ptr<function, array<f32, 4>>, i : i32) -> f32 {
  let p1 = p;
  var x = i;
  let p2 = &((*p1)[x]);
  x = 0;
  *p2 = 5;
  return (*p1)[x]; 
}

// foo의 분석용 등가 버전.
fn foo_for_analysis(p : ptr<function, array<f32, 4>>, i : i32) -> f32 {
  var p_var = *p;            // p를 위한 변수 도입
  let p1 = &p_var;           // p1에 변수 사용
  var x = i;
  let x_tmp1 = x;            // x 값 캡처
  let p2 = &(p_var[x_tmp1]); // p1 초기화식 치환
  x = 0;
  *(&(p_var[x_tmp1])) = 5;   // p2 초기화식 치환
  return (*(&p_var))[x];     // p1 초기화식 치환
}

15.2.5. 함수 범위 변수 값 분석

특정 문장에서 각 함수 범위(function-scope) 변수의 값은 해당 변수에 도달하는 할당과, 잠재적으로 초기값을 기준으로 분석할 수 있습니다.

할당은 다음 조건을 만족하면 전체 할당(full assignment)입니다:

그 외의 할당은 부분 할당(partial assignment)입니다.

전체 참조(full reference)참조 타입의 표현식 중 다음 중 하나입니다:

전체 포인터(full pointer)포인터 타입의 표현식 중 다음 중 하나입니다:

참고: 본 분석 목적상, 포인터 타입의 형식 파라미터가 전체 포인터인 경우는 필요하지 않습니다.

전체 참조전체 포인터 모두, 해당 원본 변수 x의 모든 메모리 위치에 대한 메모리 뷰입니다.

전체 참조가 아닌 참조는 부분 참조(partial reference)입니다. 부분 참조는 다음 중 하나입니다:

참고: 부분 참조도 전체 참조와 동일한 메모리 위치를 모두 포함할 수 있습니다. 이는 store type이 단일 멤버 구조체이거나, store type이 단일 요소 배열일 때 발생할 수 있습니다.

단일 멤버 구조체 타입과 해당 타입을 저장하는 변수를 생각해 보세요:

struct S { member: i32; }
fn foo () {
   var v: S;
}

이때 v는 전체 참조이고 v.member는 부분 참조입니다. 이들의 메모리 뷰는 같은 메모리 위치를 포함하지만, v의 store type은 S이고 v.s의 store type은 i32입니다.

단일 요소 배열의 경우도 유사합니다:

fn foo () {
   var arr: array<i32,1>;
}

이때 arr는 전체 참조이고 arr[0]는 부분 참조입니다. 이들의 메모리 뷰는 같은 메모리 위치를 포함하지만, arr의 store type은 array<i32,1>이고 arr[0]의 store type은 i32입니다.

분석을 단순화하기 위해, 모든 종류의 부분 참조를 통한 할당은 해당 원본 변수의 모든 메모리 위치를 수정하지 않는 것으로 간주합니다. 이는 분석을 보수적으로 만들며, 실제로 필요 이상으로 더 많은 프로그램에서 균일성 실패를 트리거할 수 있습니다.

전체 참조를 통한 할당은 전체 할당입니다.

부분 참조를 통한 할당은 부분 할당입니다.

이후 섹션의 균일성 규칙에서 RHSValue로 사용되는 함수 범위 변수의 값이란, RHSValue 표현식 평가 이전의 변수 값을 의미합니다. 이후 섹션의 균일성 규칙에서 LHSValue로 사용되는 함수 범위 변수의 값이란, 해당 표현식이 나타나는 문장 실행 이후의 변수 값을 의미합니다.

제어 흐름 문장이나 부분 할당으로 인해, 변수에 여러 할당이 도달할 수 있습니다. 분석은 제어 흐름문으로부터 도달하는 여러 할당을 각 제어 흐름 종료점에 도달하는 할당 집합의 합집합(union)으로 결합합니다.

다음 표는 할당 결합 규칙을 설명합니다. 균일성 그래프에서 각 결합은 결과 노드에서 값 소스 노드로의 엣지입니다. 임의 변수 x에 대해 쓰여집니다. 다음 표기를 사용합니다:

함수 범위 변수에 여러 할당이 도달할 때의 결합 규칙
문장 결과 결과에서의 엣지
var x; Vin(next) V(0)
var x = e;
Vin(next) V(e)

참고: 이는 전체 할당입니다.

x = e;
r = e;
여기서 r은 변수 x에 대한 전체 참조
r = e;
여기서 r은 변수 x에 대한 부분 참조
Vout(S) V(e), V(prev)

참고: 이는 부분 할당입니다.

참고: 부분 할당은 이전 값을 포함합니다. 할당이 저장된 컴포넌트의 부분집합에만 쓰거나, 할당된 값의 타입이 store type과 다를 수 있습니다.

s1 s2
여기서 Nexts1의 동작에 포함됨

참고: s1은 종종 세미콜론으로 끝남.

Vin(s2) Vout(s1)
if e s1 else s2
여기서 Nexts1s2의 동작에 모두 포함됨
Vin(next) Vout(s1), Vout(s2)
if e s1 else s2
여기서 Nexts1의 동작에만 포함됨
Vin(next) Vout(s1)
if e s1 else s2
여기서 Nexts2의 동작에만 포함됨
Vin(next) Vout(s2)
loop { s1 continuing { s2 } } Vin(s1) Vout(prev), Vout(s2)
loop { s1 continuing { s2 } } Vin(s2) Vout(s1),
Vout(si)
s1에 있는 모든 si 중 동작이 {Continue}이고 s2로 제어를 전달하는 경우
loop { s1 continuing { s2 } } Vin(next) Vout(s2),
Vout(si)
s1에 있는 모든 si 중 동작이 {Break}이고 next로 제어를 전달하는 경우
switch e {
case _: s1
case _: s2
...
case _: s3
}
Vin(si) Vout(prev)
switch e {
case _: s1
case _: s2
...
case _: s3
}
Vin(next) Vout(si),
동작에 Next 또는 Break가 포함된 모든 si에 대해,
Vout(sj)
sj 내부에서 동작이 {Break}인 모든 문장에 대해 next로 제어를 전달하는 경우

기타 모든 문장(함수 호출 제외)에 대해서는 Vin(next)은 Vout(prev)과 동일합니다.

참고: 동일한 디슈가(de-sugaring) 규칙은 문장 동작 분석에서도 적용됩니다.

15.2.6. 문장에 대한 균일성 규칙

문장 분석 규칙은 문장 자체와 그 시작 시점의 제어 흐름에 해당하는 노드(CF)를 인자로 받아, 아래 두 가지를 반환합니다:

아래 표에서 (CF1, S) => CF2는 "S에 대해 제어 흐름 CF1로 분석을 실행하고, 그래프에 필요한 변경을 적용하며, 결과 제어 흐름을 CF2로 명명한다"를 의미합니다. 마찬가지로 (CF1, E) => (CF2, V)는 "표현식 E에 대해 제어 흐름 CF1로 분석을 실행하고, 필요한 그래프 변경을 적용하며, 결과 제어 흐름 노드를 CF2, 결과 값 노드를 V로 명명한다"를 의미합니다(표현식 분석은 다음 섹션 참조). 이러한 표현식 평가는 대입문좌변이 아닌 모든 표현식에 적용되며, RHSValue라 합니다.

대입문의 좌변 표현식에 대해서도 유사한 규칙 집합이 있는데, LHSValue라 하며, LHSValue: (CF, E) => (CF, L)로 표기합니다. 이는 값의 균일성이 아니라, 우리가 접근하는 변수의 균일성에 해당하는 노드를 계산합니다.

참고: LHSValues에는 증가문감소문의 표현식도 포함됩니다.

참고: RHSValues에는 대입문 우변의 표현식이나 대입, 증가문, 감소문에 속하지 않는 모든 표현식이 포함됩니다.

여러 개의 에지를 생성해야 할 때 X -> {Y, Z}X -> Y, X -> Z의 축약형으로 사용됩니다.

문장에 대한 균일성 규칙
문장 새로운 노드 재귀적 분석 결과 제어 흐름 노드 새로운 에지
{s} (CF, s) => CF' CF'
s1 s2,
Next가 s1의 동작에 포함

참고: s1은 종종 세미콜론으로 끝납니다.

(CF, s1) => CF1
(CF1, s2) => CF2
CF2
s1 s2,
Next가 s1의 동작에 포함되지 않음

참고: s1은 종종 세미콜론으로 끝납니다.

(CF, s1) => CF1

참고: s2는 정적으로 도달 불가이며 재귀적으로 분석하지 않습니다. s2는 균일성 분석에 기여하지 않습니다.

CF1
if e s1 else s2
동작 {Next} 포함
(CF, e) => (CF', V)
(V, s1) => CF1
(V, s2) => CF2
CF
if e s1 else s2
다른 동작
CFend CFend CFend -> {CF1, CF2}
loop {s1 continuing {s2}}
동작 {Next} 포함
CF' (CF', s1) => CF1
(CF1, s2) => CF2
CF CF' -> {CF2, CF}
loop {s1 continuing {s2}}
다른 동작
CF'
loop {s1}
동작 {Next} 포함
CF' (CF', s1) => CF1 CF CF' -> {CF1, CF}
loop {s1}
다른 동작
CF'
switch e case _: s_1 .. case _: s_n
동작 {Next} 포함
(CF, e) => (CF', V)
(V, s_1) => CF_1
...
(V, s_n) => CF_n
CF
switch e case _: s_1 .. case _: s_n
다른 동작
CFend CFend CFend -> {CF_1, ..., CF_n}
var x: T; CF 참고: x가 function 주소 공간 변수일 때, CF값 분석에서 0값 초기화에 사용됩니다.
break;
continue;
break if e; (CF, e) => (CF', V) CF'
return; CF function 주소 공간 포인터 파라미터 i에 대해, Value_return_i_contents -> Vin(prev) (§ 15.2.5 함수 범위 변수 값 분석 참고)
return e; (CF, e) => (CF', V) CF' Value_return -> V

function 주소 공간 포인터 파라미터 i에 대해, Value_return_i_contents -> Vin(prev) (§ 15.2.5 함수 범위 변수 값 분석 참고)

e1 = e2; LHSValue: (CF, e1) => (CF1, LV)
(CF1, e2) => (CF2, RV)
CF2 LV -> RV

참고: LV값 분석의 결과 값입니다.

_ = e (CF, e) => (CF', V) CF'
let x = e; (CF, e) => (CF', V) CF'
var x = e; (CF, e) => (CF', V) CF' 참고: x가 function 주소 공간 변수인 경우, V값 분석의 결과 값으로 사용됩니다.

forwhile 반복문의 분석은 각각 loop 문으로 변환된 디슈가 규칙을 따릅니다.

switch에서 default-alone 절 블록은 균일성 측면에서 case 절과 동일하게 취급됩니다.

성능을 극대화하기 위해 구현체는 비균일 제어 흐름을 최소화하려고 시도합니다. 하지만 호출이 균일하다고 볼 수 있는 지점은 여러 요인에 따라 달라집니다. WGSL의 정적 분석은 if, switch, loop 문 끝에서 {Next} 동작일 경우 균일 제어 흐름으로 복귀한다고 보수적으로 가정합니다. 이는 위 표에서 결과 제어 흐름 노드가 입력 제어 흐름 노드와 동일하게 모델링됩니다.

15.2.7. 함수 호출의 균일성 규칙

가장 복잡한 규칙은 함수 호출에 대한 것입니다:

참고: Vout(call)의 정의는 § 15.2.5 함수 범위 변수 값 분석을 참고하세요.

대부분의 내장 함수는 다음과 같은 태그를 가집니다:

예외 리스트는 다음과 같습니다:

참고: WGSL 구현체는 함수 호출 이전의 제어 흐름이 균일하다면, 호출 이후에도 균일하게 유지됨을 보장합니다.

15.2.8. 표현식의 균일성 규칙

표현식 분석 규칙은 표현식 자체와 그 시작 시점의 제어 흐름 노드(CF)를 인자로 받아, 다음을 반환합니다:

RHSValue 표현식에 대한 균일성 규칙
표현식 새로운 노드 재귀적 분석 결과 제어 흐름 노드, 값 노드 새로운 에지
e1 || e2 (CF, e1) => (CF1, V1)
(V1, e2) => (CF2, V2)
CF, V2
e1 && e2
리터럴 CF, CF
identifier function-scope 변수 "x"로 해석됨, 해당 identifier가 memory view 표현식 MVE의 루트 identifier로 등장하며, load ruleMVE에서 타입 체크 중 호출됨 Result X는 이 표현식을 포함하는 문장의 입력 시점에서 "x" 값에 해당하는 노드 CF, Result Result -> {CF, X}

참고: X는 "x"에 대한 Vout(prev)과 같음
(§ 15.2.5 함수 범위 변수 값 분석 참고)

identifier function-scope 변수 "x"로 해석됨, "x"가 디슈가된 포인터 파라미터 i이며, 해당 identifier가 memory view 표현식 MVE의 루트 identifier로 등장하며, load ruleMVE에서 타입 체크 중 호출되지 않음 CF, param_i
identifier function-scope 변수 "x"로 해석됨, 해당 identifier가 memory view 표현식 MVE의 루트 identifier로 등장하며, load ruleMVE에서 타입 체크 중 호출되지 않음 CF, CF
identifier const-declaration, override-declaration, let-declaration, 또는 포인터가 아닌 타입의 non-built-in formal parameter "x"로 해석됨 Result X는 "x"에 해당하는 노드 CF, Result Result -> {CF, X}
identifier storage, workgroup, private 주소 공간의 포인터 타입 formal parameter "x"로 해석됨, non-read-only 접근 모드이며, identifier가 memory view 표현식 MVE의 루트 identifier로 등장, load ruleMVE에서 타입 체크 중 호출됨 CF, MayBeNonUniform
identifier storage, workgroup, private 주소 공간의 포인터 타입 formal parameter "x"로 해석됨, non-read-only 접근 모드이며, identifier가 memory view 표현식 MVE의 루트 identifier로 등장, load ruleMVE에서 타입 체크 중 호출되지 않음 CF, CF
identifier function 주소 공간이 아닌 주소 공간의 포인터 타입 formal parameter "x"로 해석됨, read-only 접근 모드 CF, CF
identifier 균일 내장 값 "x"로 해석됨 CF, CF
identifier 비균일 내장 값 "x"로 해석됨 CF,
MayBeNonUniform
identifier 모듈 범위 읽기 전용 변수 "x"로 해석됨 CF, CF
identifier 모듈 범위 비읽기 전용 변수 "x"로 해석됨, identifier가 memory view 표현식 MVE의 루트 identifier로 등장, load ruleMVE에서 타입 체크 중 호출됨 CF,
MayBeNonUniform
identifier 모듈 범위 비읽기 전용 변수 "x"로 해석됨, identifier가 memory view 표현식 MVE의 루트 identifier로 등장, load ruleMVE에서 타입 체크 중 호출되지 않음 CF,CF
op e,
op가 단항 연산자일 때
(CF, e) => (CF', V) CF', V
e.field
e1 op e2,
op가 단락 평가가 아닌 이항 연산자일 때
Result (CF, e1) => (CF1, V1)
(CF1, e2) => (CF2, V2)
CF2, Result Result -> {V1, V2}
e1[e2]

다음 내장 입력 변수는 균일한 것으로 간주됩니다:

그 외 모든 내장 입력 값(built-in values 참고)은 비균일로 간주됩니다.

참고: 작성자는 균일 내장 값을 다른 비균일 입력과 함께 그룹화하는 것을 피해야 합니다. 분석은 컴포넌트별로 합성 타입을 별도로 분석하지 않습니다.

LHSValue 표현식에 대한 균일성 규칙
표현식 새로운 노드 재귀적 분석 결과 제어 흐름 노드, 변수 노드 새로운 에지
identifier function-scope 변수 "x"로 해석됨 Result X는 이 표현식을 포함하는 문장의 출력 시점에서 "x" 값에 해당하는 노드. CF, Result Result -> {CF, X}

참고: X는 "x"에 대한 Vin(next)과 같음
(§ 15.2.5 함수 범위 변수 값 분석 참고)

identifier const-declaration, override-declaration, let-declaration, 또는 formal parameter "x"로 해석됨 X는 "x"에 해당하는 노드 CF, X
identifier 모듈 범위 변수 "x"로 해석됨 CF,
MayBeNonUniform
e.field LHSValue: (CF, e) => (CF1, L1) CF1, L1
*e
&e
e1[e2] LHSValue: (CF, e1) => (CF1, L1)
(CF1, e2) => (CF2, V2)
CF2, L1 L1 -> V2

15.2.9. 모든 제어 흐름 지점의 균일성 주석 달기

이 전체 하위 섹션은 규범적인 내용이 아닙니다.

구현자가 셰이더 전체의 제어 흐름 각 지점에서 해당 지점이 균일한지 여부(따라서 그 지점에서 균일성이 요구되는 함수를 호출해도 유효한지 여부)를 진단 모드로 개발자에게 보여주고 싶다면, 아래를 제안합니다:

이러한 도달성 분석에 의해 방문되지 않은 노드는 분석에 의해 균일성임이 증명될 수 있습니다(그래서 그 지점에서 도함수 등 함수를 호출해도 안전합니다).

참고: 그래프 내 호출을 만날 때 추가할 에지를 알 수 있으므로, 하향식(bottom-up) 분석은 여전히 필요합니다.

15.2.10. 예제

다음 예제의 그래프는 노드에 대해 다음과 같은 관례를 사용합니다:

15.2.10.1. 잘못된 textureSample 함수 호출

이 예제는 textureSample 내장 함수 호출의 잘못된 사용을 보여줍니다. 함수 호출이 if 문 내에서 이루어지며, 조건이 비균일 값(즉, 내장 값 position)에 따라 결정됩니다. 잘못된 의존성 체인은 빨간색으로 강조됩니다.

예제: WGSL 잘못된 textureSample
@group(0) @binding(0) var t : texture_2d<f32>;
@group(0) @binding(1) var s : sampler;

@fragment
fn main(@builtin(position) pos : vec4<f32>) {
  if (pos.x < 0.5) {
    // 잘못된 textureSample 함수 호출.
    _ = textureSample(t, s, pos.xy);
  }
}
균일성 그래프

이 예제는 if 문 이후 제어 흐름의 균일성이 if 문 이전과 동일함도 보여줍니다(CF_return이 CF_start에 연결됨). 즉, 엔트리 포인트 시작에서 균일 제어 흐름이 보장되므로 if 문 이후 제어 흐름은 다시 균일해집니다. 만약 textureSample 함수 호출이 if 문 밖에 있었다면 프로그램은 유효했을 것입니다. 마찬가지로, if 문의 조건이 균일 값(예: 모든 호출이 uniform buffer에서 동일 값을 읽는 경우)이었다면 프로그램 역시 유효했을 것입니다.

15.2.10.2. 함수 범위 변수의 균일성

이 예제는 barrier 함수 호출이 함수 범위 변수 값에 의존하는 경우의 유효 및 무효 예시를 보여줍니다. workgroupBarrierx 값이 변경 가능한 모듈 범위 변수 a에서 유도되었으므로 무효입니다. storageBarrierx 값이 불변 모듈 범위 변수 b에서 유도되었으므로 유효합니다. 이 예제는 함수 범위 변수의 생명주기에서 균일성의 구간을 분석하는 값 분석의 능력을 강조합니다. 또한 첫 번째 if 문 끝에서 제어 흐름이 다시 균일해짐을 분명하게 보여줍니다. 해당 그래프 부분이 두 번째 if 문과 독립적임을 알 수 있습니다.

예제: WGSL 함수 변수 사용
@group(0) @binding(0) var<storage, read_write> a : i32;
@group(0) @binding(1) var<uniform> b : i32;

@compute @workgroup_size(16,1,1)
fn main() {
  var x : i32;
  x = a;
  if x > 0 {
    // 잘못된 barrier 함수 호출.
    workgroupBarrier();
  }
  x = b;
  if x < 0 {
    // 유효한 barrier 함수 호출.
    storageBarrier();
  }
}
균일성 그래프

참고: 그래프 내 부분 그래프는 이해를 돕기 위해 예제에만 포함되었습니다.

15.2.10.3. 합성 값 분석의 한계

균일성 분석의 한계 중 하나는 합성 값의 각 컴포넌트를 독립적으로 추적하지 않는다는 점입니다. 즉, 비균일 컴포넌트 값이 있으면 분석은 전체 합성 값을 비균일로 처리합니다. 이 예제는 해당 문제와 셰이더 개발자가 이 한계를 피하기 위해 사용할 수 있는 잠재적 우회 방법을 보여줍니다.

예제: 잘못된 합성 값 WGSL
struct Inputs {
  // workgroup_id는 균일 내장 값입니다.
  @builtin(workgroup_id) wgid : vec3<u32>,
  // local_invocation_index는 비균일 내장 값입니다.
  @builtin(local_invocation_index) lid : u32
}

@compute @workgroup_size(16,1,1)
fn main(inputs : Inputs) {
  // 이 비교는 항상 균일하지만, 분석은 이를 알 수 없습니다.
  if inputs.wgid.x == 1 {
    workgroupBarrier();
  }
}
잘못된 균일성 그래프

이 분석의 한계를 우회하는 가장 쉬운 방법은 합성 값을 분리하여, 균일임이 알려진 값과 비균일임이 알려진 값을 각각 따로 파라미터로 받는 것입니다. 아래 대체 WGSL에서는 두 내장 값을 별도 파라미터로 분리함으로써 균일성 분석을 통과합니다. 그래프에 RequiredToBeUniform.S에서 MayBeNonUniform으로 가는 경로가 사라진 것을 볼 수 있습니다.

예제: 유효한 대체 WGSL
@compute @workgroup_size(16,1,1)
fn main(@builtin(workgroup_id) wgid : vec3<u32>,
        @builtin(local_invocation_index) lid : u32) {
  // 균일성 분석은 이제 이 비교가 항상 균일임을 정확히 판단할 수 있습니다.
  if wgid.x == 1 {
    // 유효한 barrier 함수 호출.
    workgroupBarrier();
  }
}
유효한 대체 균일성 그래프
15.2.10.4. 루프 내 균일성

이 예제에서는 루프 내에 잘못된 workgroupBarrier 함수 호출이 있습니다. 비균일 내장 값 local_invocation_index가 궁극적 원인인데, 이는 barrier 이후에 등장함에도 불구하고 무효가 됩니다. 그 이유는 이후 반복에서 워크그룹 내 일부 호출은 루프를 미리 종료하고, 다른 호출은 barrier를 실행하려 시도하기 때문입니다. 분석은 반복 간 의존성을 에지로 모델링하는데, 루프 본문 시작의 제어 흐름(CF_loop_body)이 루프 본문 끝의 제어 흐름(CF_after_if)에 의존합니다.

예제: 루프 균일성 WGSL
@compute @workgroup_size(16,1,1)
fn main(@builtin(local_invocation_index) lid : u32) {
  for (var i = 0u; i < 10; i++) {
    workgroupBarrier();
    if (lid + i) > 7 {
      break;
    }
  }
}
균일성 그래프
15.2.10.5. 사용자 정의 함수 호출

이 예제는 첫 번째 예제를 변형한 것으로, 사용자 정의 함수 호출을 사용합니다. 분석은 scale의 두 파라미터에 대해 parameter return tagParameterReturnContentsRequiredToBeUniform로 설정합니다. 이로 인해 main에서 scale 함수 호출의 반환 값에서 position 내장 값까지의 경로가 생깁니다. 해당 경로는 전체적으로 RequiredToBeUniform.S에서 MayBeNonUniform으로 이어지는 잘못된 경로의 하위 경로입니다.

예시: 사용자 정의 함수 호출의 균일성 WGSL
fn scale(in1 : f32, in2 : f32) -> f32 {
  let v = in1 / in2;
  return v;
}

@group(0) @binding(0) var t : texture_2d<f32>;
@group(0) @binding(1) var s : sampler;

@fragment
fn main(@builtin(position) pos : vec4<f32>) {
  let tmp = scale(pos.x, 0.5);
  if tmp > 1.0 {
    _ = textureSample(t, s, pos.xy);
  }
}
scale 함수의 균일성 그래프
main 함수의 균일성 그래프

참고: 서브그래프는 이해를 돕기 위한 예시로만 포함되어 있습니다.

15.3. 컴퓨트 셰이더와 워크그룹

워크그룹이란 동시에 컴퓨트 셰이더 스테이지엔트리 포인트를 실행하며, workgroup 주소 공간의 셰이더 변수에 대한 접근을 공유하는 호출 집합을 의미합니다.

컴퓨트 셰이더의 워크그룹 그리드란 정수 좌표 (i,j,k)로 이루어진 점들의 집합으로, 다음을 만족합니다:

여기서 (workgroup_size_x, workgroup_size_y, workgroup_size_z)는 엔트리 포인트의 workgroup_size 속성에 지정된 값입니다.

워크그룹 그리드의 각 점마다 반드시 하나의 호출이 워크그룹 안에 존재합니다.

호출의 로컬 호출 ID란 해당 호출이 대응하는 워크그룹 그리드 점의 좌표 (i,j,k)입니다.

호출이 로컬 호출 ID를 가질 때, 그 로컬 호출 인덱스는 다음과 같습니다:

i + (j * workgroup_size_x) + (k * workgroup_size_x * workgroup_size_y)

워크그룹에 W개의 호출이 있다면, 각 호출 I는 고유한 로컬 호출 인덱스 L(I)를 가지며 0 ≤ L(I) < W 범위를 모두 포함합니다.

컴퓨트 셰이더는 WebGPU 구현이 큐에서 디스패치 명령을 제거하고 GPU에서 지정된 작업을 시작할 때 실행이 시작됩니다. 디스패치 명령은 디스패치 크기를 지정하며, 이는 정수 삼중(group_count_x, group_count_y, group_count_z)로, 실행될 워크그룹의 수를 나타냅니다(아래 설명 참조).

특정 디스패치의 컴퓨트 셰이더 그리드란 정수 좌표 (CSi,CSj,CSk)로 이루어진 점들의 집합으로 다음을 만족합니다:

여기서 workgroup_size_x, workgroup_size_y, workgroup_size_z는 위에서 언급한 컴퓨트 셰이더 엔트리 포인트의 값입니다.

컴퓨트 셰이더 디스패치가 수행해야 할 작업은 컴퓨트 셰이더 그리드의 각 점마다 엔트리 포인트를 정확히 한 번 호출하는 것입니다.

호출의 글로벌 호출 ID란 해당 호출이 대응하는 컴퓨트 셰이더 그리드 점의 좌표 삼중입니다.

호출들은 워크그룹으로 조직되며, 각 호출의 글로벌 호출 ID (CSi, CSj, CSk)는 다음과 같이 단일 워크그룹에 매핑됩니다(워크그룹 ID):

( ⌊ CSi ÷ workgroup_size_x ⌋, ⌊ CSj ÷ workgroup_size_y ⌋, ⌊ CSk ÷ workgroup_size_z ⌋)

해당 워크그룹 내 단일 호출은 로컬 호출 ID로 식별됩니다:

( CSi mod workgroup_size_x , CSj mod workgroup_size_y , CSk mod workgroup_size_z ).

참고: 워크그룹 ID는 (0,0,0)부터 (group_count_x - 1, group_count_y - 1, group_count_z - 1)까지 범위를 가집니다.

WebGPU는 다음에 대해 아무런 보장도 하지 않습니다:

15.4. 프래그먼트 셰이더와 헬퍼 호출

프래그먼트 셰이더 스테이지의 호출들은 X 및 Y 차원에서 인접한 포지션을 가진 2x2 그리드로 나뉩니다. 이러한 각 그리드를 쿼드(quad)라고 합니다. 쿼드는 일부 집합 연산에서 협업할 수 있습니다(자세한 내용은 § 15.6.2 도함수 참고). 호출의 쿼드 호출 ID란 쿼드 내에서 고유한 ID로 다음과 같이 정의됩니다:

참고: 쿼드 ID에 접근하는 내장 값 accessor는 존재하지 않습니다.

일반적으로 프래그먼트 처리RasterizationPoint마다 하나의 프래그먼트 셰이더 호출을 생성합니다 (래스터화에서 생성됨). 때때로 그래픽 프리미티브의 경계에서 쿼드를 완전히 채울 만큼의 RasterizationPoint가 부족할 수 있습니다. 쿼드에 1, 2, 또는 3개의 호출만 RasterizationPoint에 대응하면, 프래그먼트 처리는 쿼드의 비어있는 위치마다 헬퍼 호출(helper invocation)을 생성합니다.

헬퍼 호출은 관찰 가능한 효과를 가지지 않으며, 오직 도함수 계산을 돕는 역할만 합니다. 따라서 헬퍼 호출에는 다음과 같은 제한이 적용됩니다:

쿼드의 모든 호출이 헬퍼 호출이 되는 경우(예: discard 문 실행으로 인해), 쿼드의 실행은 종료될 수 있습니다. 하지만 이러한 종료는 비균일 제어 흐름을 일으키는 것으로 간주되지 않습니다.

15.5. 서브그룹

서브그룹(subgroup)이란 동시에 컴퓨트 또는 프래그먼트 셰이더 스테이지의 엔트리 포인트를 실행하고, 데이터를 효율적으로 공유하며 집합적으로 결과를 계산할 수 있는 호출 집합입니다. 컴퓨트 또는 프래그먼트 셰이더의 각 호출은 정확히 하나의 서브그룹에 속합니다. 컴퓨트 셰이더에서는 각 서브그룹이 특정 워크그룹의 부분집합입니다. 프래그먼트 셰이더에서는 서브그룹이 여러 draw 명령의 호출을 포함할 수 있습니다. 각 쿼드는 하나의 서브그룹에 포함됩니다.

서브그룹 크기(subgroup size)란 서브그룹 내 호출의 최대 개수입니다. 셰이더 내에서는 subgroup_size 내장 값으로 접근할 수 있습니다. 서브그룹 크기는 디스패치 명령 내에서, 따라서 워크그룹 내에서 균일 값이지만, draw 명령 내에서는 균일 값이 아닐 수 있습니다. 모든 서브그룹 크기는 [4, 128] 범위의 2의 거듭제곱이며, 특정 디바이스에 대해 컴파일된 셰이더의 값은 subgroupMinSize에서 subgroupMaxSize 범위에 있습니다(WebGPU § 4.3 GPUAdapter 참고). 실제 크기는 셰이더, 디바이스 속성, 디바이스 컴파일러에 따라 달라집니다. 각 디바이스는 가능한 서브그룹 크기 범위 중 일부(혹은 하나의 값만)를 지원합니다. 디바이스 컴파일러는 다양한 휴리스틱을 사용해 지원되는 크기 중 하나를 선택합니다. 실제 서브그룹 내 호출 수가 보고된 서브그룹 크기보다 작을 수도 있습니다 (예: 실제 실행된 호출 수가 서브그룹 크기보다 적은 경우).

호출의 서브그룹 호출 ID(subgroup invocation ID)는 서브그룹 내에서의 고유 ID입니다. 이 ID는 subgroup_invocation_id 내장 값으로 접근할 수 있으며, [0, subgroup_size - 1] 범위입니다. subgroup_invocation_idlocal_invocation_index 사이에는 정의된 관계가 없습니다. 코드의 이식성을 위해 셰이더 작성자는 두 값 사이의 특정 매핑을 가정해서는 안 됩니다.

같은 서브그룹 내 호출이 서로 다른 제어 흐름 경로를 실행할 때, 서브그룹 실행이 분기(diverge)했다고 합니다. 이것은 비균일 제어 흐름의 특수한 경우입니다. 분기는 서브그룹 연산의 의미에 영향을 미칩니다. 서브그룹 내에서 집합 연산을 동시에 실행하는 호출은 해당 연산의 활성(active) 호출입니다. 서브그룹 내 다른 호출은 해당 연산의 비활성(inactive) 호출입니다. 서브그룹 크기가 서브그룹 내 호출 수를 초과하면, 추가 가상의 호출은 비활성으로 간주됩니다. 헬퍼 호출도 연산에서 활성 또는 비활성일 수 있습니다. 즉, 어떤 디바이스에서는 헬퍼 호출이 서브그룹 연산에 참여할 수 있고, 다른 디바이스에서는 그렇지 않을 수 있습니다.

참고: 비균일 제어 흐름에서 동작할 때, 각 디바이스마다 이식성에 많은 차이가 있으며, 디바이스 컴파일러는 이런 코드를 공격적으로 최적화하는 경우가 많습니다. 그 결과, 서브그룹 내 활성 호출 집합이 셰이더 작성자가 기대하는 것과 다를 수 있습니다.

15.6. 집합 연산

15.6.1. 배리어(Barrier)

배리어는 프로그램 내 메모리 연산의 순서를 지정하는 동기화 내장 함수입니다. 제어 배리어(control barrier)는 같은 워크그룹 내 모든 호출이 마치 동시에 실행하는 것처럼 실행됩니다. 따라서 제어 배리어는 반드시 컴퓨트 셰이더에서 균일 제어 흐름에서만 실행되어야 합니다.

15.6.2. 도함수(Derivatives)

편미분(partial derivative)이란 어떤 축을 따라 값이 얼마나 변하는지를 나타냅니다. 프래그먼트 셰이더의 같은 쿼드 내 호출들은 근사 편미분을 공동 계산합니다.

도함수를 계산하는 내장 함수는 다음과 같습니다:

다음 내장 함수에서는 프래그먼트 좌표(fragment coordinate)의 편미분이 암묵적으로 계산됩니다:

이 경우, 도함수는 샘플링할 texel의 mip level을 결정하거나, textureSampleCompare의 경우 참조 값과 비교하는 데 사용됩니다.

호출이 지정한(invocation-specified) 값의 편미분은 § 17.6 Derivative 내장 함수에 설명된 내장 함수로 계산됩니다:

인접 호출들이 함께 도함수를 계산하므로, 이 함수들은 프래그먼트 셰이더에서 반드시 균일 제어 흐름에서만 호출해야 합니다. 이러한 함수 각각의 호출에 대해, 만약 균일성 분석이 호출이 균일 제어 흐름에서 발생하는지 증명하지 못한다면, derivative_uniformity 진단이 트리거됩니다.

이 함수들이 비균일 제어 흐름에서 호출되면, 그 결과는 불확정 값(indeterminate value)입니다.

참고: 도함수는 암묵적으로 쿼드 연산의 한 종류입니다. 이 함수들을 사용하기 위해 subgroups 확장(extension)이 필요하지 않습니다.

15.6.3. 서브그룹 연산

서브그룹 내장 함수서브그룹 내 호출 사이의 효율적인 통신과 계산을 제공합니다. 서브그룹 연산은 단일 명령어 다중 스레드(SIMT) 연산입니다.

활성 호출들은 서브그룹 내에서 결과를 결정하기 위해 통신합니다. 따라서 이러한 함수들은 모든 호출이 활성 상태(즉, 서브그룹 수준에서 균일 제어 흐름에 있을 때) 호출하는 것이 이식성을 극대화합니다.

15.6.4. 쿼드 연산

쿼드 내장 함수쿼드 내 호출을 대상으로 동작합니다. 이 함수들은 쿼드 내 데이터 통신에 유용합니다.

활성 호출들은 쿼드 내에서 결과를 결정하기 위해 통신합니다. 따라서 이러한 함수들은 모든 호출이 활성 상태(즉, 쿼드 수준에서 균일 제어 흐름에 있을 때) 호출하는 것이 이식성을 극대화합니다.

15.7. 부동소수점 연산

WGSL의 부동소수점 기능은 IEEE-754 부동소수점 표준을 기반으로 하지만, GPU의 타협에 따라 기능이 축소되어 있고, 이식성을 위한 추가적인 보호장치들이 있습니다.

15.7.1. IEEE-754 개요

WGSL의 부동소수점 타입은 IEEE-754 이진 부동소수점 타입을 기반으로 합니다.

IEEE-754 이진 부동소수점 타입은 확장 실수 수직선을 다음과 같이 근사합니다:

부동소수점 타입의 유한 범위(finite range)구간(interval) [low, high]으로, low는 타입 내에서 가장 작은 유한 값이고, high는 가장 큰 유한 값입니다.

관심 있는 IEEE-754 부동소수점 타입은 다음과 같습니다:

다음 알고리즘은 부동소수점 값의 비트 표현을 해당 확장 실수 값이나 NaN으로 매핑합니다:

알고리즘: 비트의 부동소수점 해석

입력: Bits, 이진 부동소수점 타입의 값에 대한 비트 표현.

출력: F, Bits가 나타내는 부동소수점 값.

절차:

부동소수점 연산의 도메인(domain)은 해당 연산이 잘 정의된 확장 실수 입력값 집합입니다.

반올림(rounding)확장 실수x를 부동소수점 타입의 값 x'로 매핑합니다. x가 이미 해당 부동소수점 타입의 값일 경우, 반올림은 x를 자기 자신으로 매핑합니다: x = x'. x가 타입의 유한 범위 밖이면 반올림은 오버플로우(overflow)가 발생할 수 있습니다. 그 외의 경우 x'x보다 위쪽에 있는 가장 작은 부동소수점 값이거나 아래쪽에 있는 가장 큰 부동소수점 값입니다; 반올림 모드(rounding mode)가 어느 쪽을 선택할지 결정합니다.

일반적으로, NaN 입력이 있는 연산은 NaN 출력을 반환합니다. 예외로는:

IEEE-754는 다섯 종류의 예외(exception)를 정의합니다:

15.7.2. IEEE-754와의 차이점

WGSL은 IEEE-754 표준을 따르지만, 다음과 같은 차이가 있습니다:

15.7.3. 부동소수점 반올림 및 오버플로우

오버플로우가 발생하는 계산은 무한대로 반올림하거나, 가장 가까운 유한 값으로 반올림할 수 있습니다. 결과는 오버플로우 중간 결과의 크기와, 평가가 셰이더 모듈 생성, 파이프라인 생성, 셰이더 실행 중 언제 일어났는지에 따라 달라집니다.

부동소수점 타입 T에 대해, MAX(T)T의 가장 큰 양의 유한 값이며, 2EMAX(T)T로 표현 가능한 가장 큰 2의 거듭제곱입니다. 예를 들어, EMAX(f32) = 127, EMAX(f16) = 15입니다.

부동소수점 계산의 무한 정확도 중간 결과 X가 있다고 할 때, 표현식의 최종 값은 두 단계의 중간 결과X', X''를 통해 결정됩니다:

X로부터 X'T에서 반올림하여 계산합니다:

X'로부터 표현식의 최종 값 X''를 계산하거나 프로그램 오류를 검출합니다:

15.7.4. 부동소수점 정확도

x가 무한정 정밀도로 연산된 연산의 정확한 실수값 혹은 무한대 결과라 하자. 부동소수점 타입 T에 대한 연산의 올바른 반올림(correctly rounded) 결과는 다음과 같다:

즉, 결과는 위/아래 반올림될 수 있다. WGSL은 반올림 모드를 지정하지 않는다.

참고: 부동소수점 타입에는 양의 무한대와 음의 무한대가 포함되므로 올바르게 반올림된 결과는 유한값 또는 무한대일 수 있다.

참고: 무한정 정밀도로 연산된 연산 결과는 double보다 높은 정밀도가 필요할 수 있다. 예를 들어 x - y에서 x=1.0이고 y=1.17e-38(단정도 float에서 가장 작은 양의 정규값)인 경우, 두 수의 지수는 126만큼 차이가 난다. IEEE-754 binary64(double precision) 형식은 가수 부호가 52비트뿐이므로, 뺄셈을 할 때 y의 모든 유효비트가 손실된다. 반올림 모드에 따라, 이처럼 y가 매우 작지만 0이 아닌 경우 x - y의 WGSL 결과는 x와 동일할 수 있다. [ECMASCRIPT]IEEE-754의 roundTiesToEven 반올림 모드를 사용한다.

마지막 자리 단위(ULP, ULP)는 x라는 부동소수점 값에 대해 다음과 같이 정의된다 [Muller2005]:

연산의 정확도는 다섯 가지 중 하나로 제공된다:

연산의 정확도가 입력 범위에 대해 지정되어 있으면, 해당 범위를 벗어난 입력값에 대한 정확도는 정의되지 않는다.

허용된 결과가 결과 타입의 유한 범위 밖에 있으면, § 15.7.3 부동소수점 반올림 및 오버플로우의 규칙이 적용된다.

15.7.4.1. 구체적 부동소수점 표현식의 정확도
구체적 부동소수점 연산의 정확도
표현식 f32의 정확도 f16의 정확도
x + y 올바른 반올림
x - y 올바른 반올림
x * y 올바른 반올림
x / y 2.5 ULP, |y|가 [2-126, 2126] 범위일 때 2.5 ULP, |y|가 [2-14, 214] 범위일 때
x % y x - y * trunc(x/y)에서 상속
-x 올바른 반올림
x == y 정확한 결과
x != y 정확한 결과
x < y 정확한 결과
x <= y 정확한 결과
x > y 정확한 결과
x >= y 정확한 결과
구체적 부동소수점 내장 함수의 정확도
내장 함수 f32의 정확도 f16의 정확도
abs(x) 올바른 반올림
acos(x) 다음 중 더 나쁜 값:
  • 절대 오차 6.77×10-5

  • atan2(sqrt(1.0 - x * x), x)에서 상속

다음 중 더 나쁜 값:
  • 절대 오차 3.91×10-3

  • atan2(sqrt(1.0 - x * x), x)에서 상속

acosh(x) log(x + sqrt(x * x - 1.0))에서 상속
asin(x) 다음 중 더 나쁜 값:
  • 절대 오차 6.81×10-5

  • atan2(x, sqrt(1.0 - x * x))에서 상속

다음 중 더 나쁜 값:
  • 절대 오차 3.91×10-3

  • atan2(x, sqrt(1.0 - x * x))에서 상속

asinh(x) log(x + sqrt(x * x + 1.0))에서 상속
atan(x) 4096 ULP 5 ULP
atan2(y, x) |x|이 [2-126, 2126] 범위이고, y가 유한하고 정규일 때 4096 ULP |x|이 [2-14, 214] 범위이고, y가 유한하고 정규일 때 5 ULP
atanh(x) log( (1.0 + x) / (1.0 - x) ) * 0.5에서 상속
ceil(x) 올바른 반올림
clamp(x,low,high) 올바른 반올림

무한정 정밀도의 결과는 min(max(x,low),high) 또는 3값의 중간값 방식으로 계산됨. low > high일 때 차이가 있을 수 있음.

x, low, high 중 하나라도 서브노멀이면, 결과는 서브노멀 값 중 아무거나일 수 있음. 이는 min, max 함수의 서브노멀 입력에 대한 가능한 결과와 동일함.

cos(x) 절대 오차 최대 2-11, x가 [-π, π] 구간일 때 절대 오차 최대 2-7, x가 [-π, π] 구간일 때
cosh(x) (exp(x) + exp(-x)) * 0.5에서 상속
cross(x, x) (x[i] * y[j] - x[j] * y[i])에서 상속(단, ij)
degrees(x) x * 57.295779513082322865에서 상속
determinant(m:mat2x2<T>)
determinant(m:mat3x3<T>)
determinant(m:mat4x4<T>)
무한대 ULP
참고:WebGPU 구현체는 실용적으로 유용한 determinant 함수를 제공해야 합니다.

이상적인 수학에서는 determinant는 덧셈, 뺄셈, 곱셈 연산으로 계산됩니다.

그러나 GPU에서는 부동소수점 연산을 사용하며, GPU의 determinant 구현은 오버플로우 및 오차에 대한 강건성보다 속도와 단순성을 우선시합니다.

예를 들어, 2x2 determinant의 순진한 계산(m[0][0] * m[1][1] - m[1][0] * m[0][1])은 심각한 상쇄(cancellation)에 대한 방어가 없습니다. 2x2 determinant의 오차 경계를 엄격하게 제공하는 것은 최근 연구 [Jeannerod2013]의 주제입니다. 행렬 크기가 커질수록 어려움이 급격히 증가합니다.

WGSL에서 determinant에 대해 유한한 오차 경계가 없는 것은 구현체에서도 동일하게 경계가 없기 때문입니다.

distance(x, y) length(x - y)에서 상속
dot(x, y) x[i] * y[i]에서 상속
dpdx(x)
dpdxCoarse(x)
dpdxFine(x)
dpdy(x)
dpdyCoarse(x)
dpdyFine(x)
fwidth(x)
fwidthCoarse(x)
fwidthFine(x)
무한대 ULP
참고:WebGPU 구현체는 실용적으로 유용한 도함수 함수를 제공해야 합니다.

도함수는 GPU에서 서로 다른 호출 간 값의 차이(혹은 fwidth의 경우 절대값의 차이)로 구현됩니다.

WGSL에서 도함수에 대해 유한 오차 경계가 없는 것은 구현체에서도 경계가 없기 때문입니다.

exp(x) 3 + 2 * |x| ULP 1 + 2 * |x| ULP
exp2(x) 3 + 2 * |x| ULP 1 + 2 * |x| ULP
faceForward(x, y, z) select(-x, x, dot(z, y) < 0.0)에서 상속
floor(x) 올바른 반올림
fma(x, y, z) x * y + z에서 상속
fract(x) x - floor(x)에서 상속
frexp(x) x가 0 또는 정규일 때 올바른 반올림
inverseSqrt(x) 2 ULP
ldexp(x, y) 올바른 반올림
length(x) 벡터의 경우 sqrt(dot(x, x)), 스칼라의 경우 sqrt(x*x)에서 상속
log(x) 0.5~2.0 구간에서는 절대 오차 최대 2-21, 그 외 구간에서는 3 ULP 0.5~2.0 구간에서는 절대 오차 최대 2-7, 그 외 구간에서는 3 ULP
log2(x) 0.5~2.0 구간에서는 절대 오차 최대 2-21, 그 외 구간에서는 3 ULP 0.5~2.0 구간에서는 절대 오차 최대 2-7, 그 외 구간에서는 3 ULP
max(x, y) 올바른 반올림

x와 y 모두 서브노멀이면, 결과는 두 입력 중 아무거나일 수 있음.

min(x, y) 올바른 반올림

x와 y 모두 서브노멀이면, 결과는 두 입력 중 아무거나일 수 있음.

mix(x, y, z) x * (1.0 - z) + y * z에서 상속
modf(x) 올바른 반올림
normalize(x) x / length(x)에서 상속
pack4x8snorm(x) 올바른 반올림 중간 결과 값. 정확한 결과.
pack4x8unorm(x) 올바른 반올림 중간 결과 값. 정확한 결과.
pack2x16snorm(x) 올바른 반올림 중간 결과 값. 정확한 결과.
pack2x16unorm(x) 올바른 반올림 중간 결과 값. 정확한 결과.
pack2x16float(x) 올바른 반올림 중간 결과 값. 정확한 결과.
pow(x, y) exp2(y * log2(x))에서 상속
quantizeToF16(x) 올바른 반올림
radians(x) x * 0.017453292519943295474에서 상속
reflect(x, y) x - 2.0 * dot(x, y) * y에서 상속
refract(x, y, z) z * x - (z * dot(y, x) + sqrt(k)) * y에서 상속,
여기서 k = 1.0 - z * z * (1.0 - dot(y, x) * dot(y, x))
k < 0.0이면 결과는 정확히 0.0
round(x) 올바른 반올림
sign(x) 올바른 반올림
sin(x) 절대 오차 최대 2-11, x가 [-π, π] 구간일 때 절대 오차 최대 2-7, x가 [-π, π] 구간일 때
sinh(x) (exp(x) - exp(-x)) * 0.5에서 상속
saturate(x) 올바른 반올림
smoothstep(edge0, edge1, x) t * t * (3.0 - 2.0 * t)에서 상속,
여기서 t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
sqrt(x) 1.0 / inverseSqrt(x)에서 상속
step(edge, x) 올바른 반올림
tan(x) sin(x) / cos(x)에서 상속
tanh(x) 다음 중 더 나쁜 값:
  • 절대 오차 1.0×10-5

  • sinh(x) / cosh(x)에서 상속

transpose(x) 올바른 반올림
trunc(x) 올바른 반올림
unpack4x8snorm(x) 3 ULP N/A
unpack4x8unorm(x) 3 ULP N/A
unpack2x16snorm(x) 3 ULP N/A
unpack2x16unorm(x) 3 ULP N/A
unpack2x16float(x) 올바른 반올림 N/A
subgroupBroadcast(x, i) 올바른 반올림
subgroupBroadcastFirst(x) 올바른 반올림
subgroupAdd(x) 서브그룹 내 모든 활성 호출에 대해 x의 합에서 상속
subgroupExclusiveAdd(x) 서브그룹 내 활성 호출 중 서브그룹 호출 ID가 현재 호출보다 작은 것들의 x의 합에서 상속
subgroupInclusiveAdd(x) 서브그룹 내 활성 호출 중 서브그룹 호출 ID가 현재 호출 이하인 것들의 x의 합에서 상속
subgroupMul(x) 서브그룹 내 모든 활성 호출에 대해 x의 곱에서 상속
subgroupExclusiveMul(x) 서브그룹 내 활성 호출 중 i번째 호출보다 ID가 작은 xi의 곱에서 상속
subgroupInclusiveMul(x) 서브그룹 내 활성 호출 중 i번째 호출 이하인 xi의 곱에서 상속
subgroupMax(x) 서브그룹 내 모든 활성 호출에 대해 max(x)에서 상속
subgroupMin(x) 서브그룹 내 모든 활성 호출에 대해 min(x)에서 상속
subgroupShuffle(x, id) 올바른 반올림
subgroupShuffleDown(x, delta) 올바른 반올림
subgroupShuffleUp(x, delta) 올바른 반올림
subgroupShuffleXor(x, mask) 올바른 반올림
quadBroadcast(x, id) 올바른 반올림
quadSwapDiagonal(x) 올바른 반올림
quadSwapX(x) 올바른 반올림
quadSwapY(x) 올바른 반올림
15.7.4.2. AbstractFloat 표현식의 정확도

AbstractFloat 연산의 정확도는 다음과 같습니다:

참고: 절대 오차 경계를 ULP로 환산할 때는 기저가 되는 부동소수점 타입에 결정적으로 의존합니다.

ULPAbstractFloat 값의 경우 AbstractFloat이 IEEE-754 binary64 타입과 동일하다고 가정합니다.

f32 값의 1 ULP는 IEEE-754 binary64 값의 1 ULP보다 229배 크며, 이는 binary64 형식의 가수가 f32 타입보다 29비트 더 길기 때문입니다.

예를 들어, 어떤 연산의 진짜 결과값이 x이고 계산된 값이 x'인 경우, 오차 x-x'가 f32에서 3 ULP라면, 동일한 절대 오차 x-x'는 AbstractFloat에서는 3·229 ULP가 됩니다.

15.7.5. 재결합(Reassociation)과 결합(Fusion)

재결합(Reassociation)이란 연산을 정확하게 계산했을 때 결과가 같은 방식으로 표현식을 재배열하는 것을 의미합니다. 예시:

하지만, 부동소수점에서 계산할 때 결과가 같지 않을 수 있습니다. 재결합된 결과는 근사 오차로 인해 부정확하거나, 중간 결과 계산 시 오버플로우 또는 NaN을 유발할 수 있습니다.

구현체는 연산을 재결합할 수 있습니다.

구현체는 변환된 표현식이 원래 표현식만큼 정확하거나 더 정확하면 연산을 결합(fuse)할 수 있습니다. 예를 들어, 일부 곱셈-덧셈 결합(fused multiply-add) 구현은 곱셈 후 덧셈을 따로 수행하는 것보다 더 정확할 수 있습니다.

15.7.6. 부동소수점 변환

이 섹션은 소스 또는 목적지가 부동소수점 타입인 스칼라 변환의 세부사항을 설명합니다.

이 섹션에서 부동소수점 타입은 다음 중 하나일 수 있습니다:

참고: f32 WGSL 타입은 IEEE-754 binary32 형식에, f16 WGSL 타입은 IEEE-754 binary16 형식에 대응함을 기억하세요.

스칼라 부동소수점 → 정수 변환 알고리즘:

부동소수점 스칼라 값 Xinteger scalar 타입 T로 변환하려면:

참고: 즉, NaN이 아닌 경우 부동소수점→정수 변환은 값을 대상 타입 범위 내로 클램핑한 뒤 0 방향으로 반올림합니다. 이 클램핑 요구사항은 WGSL이 의미 있는 결과를 요구하는 부분이며, C/C++에서는 미정의 동작이고, IEEE-754에서는 잘못된 연산 예외 + NaN 결과를 요구합니다.

참고: 예시:

수치 스칼라 → 부동소수점 변환 알고리즘:

알고리즘: 수치 스칼라 변환 → 부동소수점

입력:

출력: XOut: XT로 변환한 결과, 또는 오류 발생

절차:

참고: 정수 값은 인접한 두 부동소수점 값 사이에 있을 수 있습니다. 예를 들어, f32 타입은 23개의 명시적 소수 비트를 사용합니다. 부동소수점 값이 정규 범위(지수가 극단값이 아님)에 있으면, 가수는 소수 비트 집합에 23비트 위치에 추가 1비트가 더해집니다. 따라서 예를 들어 228과 1+228은 같은 부동소수점 값에 매핑되며, 최소 225 크기의 인접 정수 쌍에서 이러한 충돌이 발생합니다.

참고: 원래 타입이 i32 또는 u32이고 목적지 타입이 f32인 경우, 원래 값은 항상 목적지 타입의 범위 내에 있습니다.

참고: 원래 타입이 지수/가수 비트가 더 적은 부동소수점 타입이고, 목적지 타입이 더 많은 부동소수점 타입이면, 원래 값은 항상 목적지 타입의 범위 내에 있습니다.

15.7.7. 부동소수점 표현식 및 내장 함수의 도메인

이전 섹션에서는 부동소수점 표현식이 도메인 밖에서 평가될 때의 기대 동작을 설명합니다.

§ 8.7 산술 표현식§ 17.5 수치 내장 함수에서는 각각 부동소수점 표현식과 내장 함수의 도메인을 정의합니다. 특정 연산에 제한이 명시되지 않은 경우, 도메인은 전체이며 모든 유한 및 무한 입력을 포함합니다. 그 외에는 명시적으로 도메인이 제시됩니다.

WGSL 연산이 IEEE-754에 정의된 연산과 대응되는 경우, 두 연산은 동일한 도메인을 가집니다. 예를 들어, WGSL과 IEEE-754의 acos 연산은 모두 [−1,1] 도메인을 가집니다.

컴포넌트별(component-wise) WGSL 연산에 대해 명시적 도메인이 있을 경우, 스칼라 케이스만 설명합니다. 벡터 케이스는 컴포넌트별 의미에서 추론합니다.

일부 WGSL 연산은 다른 WGSL 표현식으로 구현될 수 있습니다. § 15.7.4 부동소수점 정확도에서는 이런 연산을 상속됨 정확도로 분류합니다. 도메인을 명시할 때, 다음 중 하나입니다:

예시: dot(a,b) 함수가 2-요소 벡터 a, b에 대해 정확도가 상속됨으로 분류됩니다. a[0] * b[0] + a[1] * b[1] 표현을 사용하며, 두 번의 부동소수점 곱셈과 한 번의 덧셈이 사용됩니다.

16. 키워드 및 토큰 요약

16.1. 키워드 요약

16.2. 예약어

예약어토큰으로, 추후 사용을 위해 예약되어 있습니다. WGSL 모듈은 예약어를 포함해서는 안 됩니다.

다음은 예약어입니다:

_reserved :

| 'NULL'

| 'Self'

| 'abstract'

| 'active'

| 'alignas'

| 'alignof'

| 'as'

| 'asm'

| 'asm_fragment'

| 'async'

| 'attribute'

| 'auto'

| 'await'

| 'become'

| 'cast'

| 'catch'

| 'class'

| 'co_await'

| 'co_return'

| 'co_yield'

| 'coherent'

| 'column_major'

| 'common'

| 'compile'

| 'compile_fragment'

| 'concept'

| 'const_cast'

| 'consteval'

| 'constexpr'

| 'constinit'

| 'crate'

| 'debugger'

| 'decltype'

| 'delete'

| 'demote'

| 'demote_to_helper'

| 'do'

| 'dynamic_cast'

| 'enum'

| 'explicit'

| 'export'

| 'extends'

| 'extern'

| 'external'

| 'fallthrough'

| 'filter'

| 'final'

| 'finally'

| 'friend'

| 'from'

| 'fxgroup'

| 'get'

| 'goto'

| 'groupshared'

| 'highp'

| 'impl'

| 'implements'

| 'import'

| 'inline'

| 'instanceof'

| 'interface'

| 'layout'

| 'lowp'

| 'macro'

| 'macro_rules'

| 'match'

| 'mediump'

| 'meta'

| 'mod'

| 'module'

| 'move'

| 'mut'

| 'mutable'

| 'namespace'

| 'new'

| 'nil'

| 'noexcept'

| 'noinline'

| 'nointerpolation'

| 'non_coherent'

| 'noncoherent'

| 'noperspective'

| 'null'

| 'nullptr'

| 'of'

| 'operator'

| 'package'

| 'packoffset'

| 'partition'

| 'pass'

| 'patch'

| 'pixelfragment'

| 'precise'

| 'precision'

| 'premerge'

| 'priv'

| 'protected'

| 'pub'

| 'public'

| 'readonly'

| 'ref'

| 'regardless'

| 'register'

| 'reinterpret_cast'

| 'require'

| 'resource'

| 'restrict'

| 'self'

| 'set'

| 'shared'

| 'sizeof'

| 'smooth'

| 'snorm'

| 'static'

| 'static_assert'

| 'static_cast'

| 'std'

| 'subroutine'

| 'super'

| 'target'

| 'template'

| 'this'

| 'thread_local'

| 'throw'

| 'trait'

| 'try'

| 'type'

| 'typedef'

| 'typeid'

| 'typename'

| 'typeof'

| 'union'

| 'unless'

| 'unorm'

| 'unsafe'

| 'unsized'

| 'use'

| 'using'

| 'varying'

| 'virtual'

| 'volatile'

| 'wgsl'

| 'where'

| 'with'

| 'writeonly'

| 'yield'

16.3. 구문 토큰

구문 토큰은 특별한 코드 포인트의 시퀀스로, 다음과 같이 사용됩니다:

구문 토큰은 다음과 같습니다:

17. 내장 함수

특정 함수들은 사전 선언되어 있으며, 구현에 의해 제공되고, 따라서 WGSL 모듈에서 항상 사용할 수 있습니다. 이러한 함수들을 내장 함수라고 합니다.

내장 함수는 동일한 이름을 가지지만, 매개변수의 개수, 순서, 타입에 따라 구분되는 함수군입니다. 이러한 각각의 함수 변형을 오버로드라고 합니다.

참고:사용자 정의 함수는 하나의 오버로드만 가집니다.

오버로드는 아래에 다음과 같이 설명됩니다:

내장 함수를 호출할 때, 함수의 모든 인자는 함수 평가가 시작되기 전에 평가됩니다. § 11.2 함수 호출을 참고하세요.

17.1. 생성자 내장 함수

값 생성자 내장 함수는 명시적으로 주어진 타입의 값을 생성합니다.

WGSL은 모든 선언된 타입과 모든 생성 가능 구조체 타입에 대해 값 생성자를 제공합니다. 생성자 내장 함수는 타입과 동일한 이름을 가집니다. 이러한 내장 함수가 사용되는 모든 곳에서, 타입의 식별자스코프 내에 있어야 하며, 식별자다른 선언으로 해결되지 않아야 합니다.

참고: frexp, modf, 그리고 atomicCompareExchangeWeak에 의해 반환되는 구조체 타입들은 WGSL 모듈에서 작성할 수 없습니다.

참고: 해당 타입의 값 선언은 WGSL 텍스트의 해당 구문에서 유효해야 합니다.

WGSL은 두 가지 종류의 값 생성자를 제공합니다:

17.1.1. 제로 값 내장 함수

각각의 구체적, 생성 가능한 T는 고유한 제로 값을 가지며, 해당 타입 이름 뒤에 빈 괄호를 붙여 WGSL에서 내장 함수로 작성됩니다: T (). 추상 수치 타입도 제로 값을 가지지만, 이를 접근할 수 있는 내장 함수는 없습니다.

제로 값은 다음과 같습니다:

참고: WGSL은 atomic 타입, 실행 시 크기가 결정되는 배열, 또는 생성 불가 타입에 대한 제로 내장 함수를 제공하지 않습니다.

오버로드
@const @must_use fn T() -> T
파라미터화 T구체적이고 생성 가능한 타입입니다.
설명 타입 T제로 값을 생성합니다.

참고: AbstractInt의 제로로 채워진 벡터는 vec2(), vec3(), vec4()로 작성할 수 있습니다.

예시: 제로 값 벡터
vec2<f32>()                 // 두 개의 f32 컴포넌트로 이루어진 제로 값 벡터
vec2<f32>(0.0, 0.0)         // 동일한 값을 명시적으로 작성

vec3<i32>()                 // 세 개의 i32 컴포넌트로 이루어진 제로 값 벡터
vec3<i32>(0, 0, 0)          // 동일한 값을 명시적으로 작성
예시: 제로 값 배열
array<bool, 2>()               // 두 개의 불리언으로 이루어진 제로 값 배열
array<bool, 2>(false, false)   // 동일한 값을 명시적으로 작성
예시: 제로 값 구조체
struct Student {
  grade: i32,
  GPA: f32,
  attendance: array<bool,4>
}

fn func() {
  var s: Student;

  // Student의 제로 값
  s = Student();

  // 동일한 값을 명시적으로 작성
  s = Student(0, 0.0, array<bool,4>(false, false, false, false));

  // 제로 값 멤버로 작성한 동일한 값
  s = Student(i32(), f32(), array<bool,4>());
}

17.1.2. 값 생성자 내장 함수

아래 하위 섹션에서 정의된 내장 함수들은 다음과 같은 방식으로 생성 가능한 값을 생성합니다:

벡터 및 행렬 형태는 다양한 구성 요소와 부분 벡터의 조합으로, 일치하는 구성 요소 타입의 벡터 및 행렬 값을 생성합니다. 벡터와 행렬을 생성할 때, 대상 타입의 차원을 지정하지만 구성 요소 타입은 지정하지 않아도 되고, 생성자 인자에서 유추됩니다. 벡터와 행렬 생성에 관한 오버로드가 제공됩니다.

17.1.2.1. array
오버로드
@const @must_use fn array<T, N>(e1 : T, ..., eN : T) -> array<T, N>
파라미터화 T구체적이고 생성 가능한 타입입니다.
설명 구성 요소로부터 배열을 생성합니다.

참고: array<T,N>는 생성 가능한 타입입니다. 왜냐하면 요소 수가 생성자에 전달되는 인자 개수와 동일하므로, 셰이더 생성 시점에 완전히 결정됩니다.

오버로드
@const @must_use fn array(e1 : T, ..., eN : T) -> array<T, N>
파라미터화 T생성 가능한 타입입니다.
설명 구성 요소로부터 배열을 생성합니다.

구성 요소 타입은 요소 타입에서 유추됩니다. 배열의 크기는 요소의 개수에 의해 결정됩니다.

17.1.2.2. bool
오버로드
@const @must_use fn bool(e : T) -> bool
파라미터화 T스칼라 타입입니다.
설명 bool 값을 생성합니다.

Tbool이면, 항등 연산입니다.
그렇지 않으면 불리언 변환입니다. e제로 값이거나(부동소수점 타입의 -0.0 포함)일 때 결과는 false이고, 그렇지 않으면 true입니다.

17.1.2.3. f16
오버로드
@const @must_use fn f16(e : T) -> f16
파라미터화 T스칼라 타입입니다.
설명 f16 값을 생성합니다.

Tf16이면, 항등 연산입니다.
T수치 스칼라(f16 제외) 타입이면, ef16으로 변환됩니다(잘못된 변환 포함).
Tbool이면, etrue일 때 1.0h, 그렇지 않으면 0.0h가 결과입니다.

17.1.2.4. f32
오버로드
@const @must_use fn f32(e : T) -> f32
파라미터화 T구체적 스칼라 타입입니다.
설명 f32 값을 생성합니다.

Tf32이면, 항등 연산입니다.
T수치 스칼라(f32 제외) 타입이면, ef32로 변환됩니다(잘못된 변환 포함).
Tbool이면, etrue일 때 1.0f, 그렇지 않으면 0.0f가 결과입니다.

17.1.2.5. i32
오버로드
@const @must_use fn i32(e : T) -> i32
파라미터화 T스칼라 타입입니다.
설명 i32 값을 생성합니다.

Ti32이면, 항등 연산입니다.
Tu32이면, 비트 재해석(즉, i32에서 e와 동일한 비트 패턴을 가지는 유일한 값이 결과)입니다.
T부동소수점 타입이면, ei32로 변환되며, 0 방향 반올림됩니다.
Tbool이면, etrue일 때 1i, 그렇지 않으면 0i가 결과입니다.
TAbstractInt이면, ei32로 표현 가능하면 항등 연산, 불가능하면 셰이더 생성 오류를 발생합니다.

17.1.2.6. mat2x2
오버로드
@const @must_use fn mat2x2<T>(e : mat2x2<S>) -> mat2x2<T>
@const @must_use fn mat2x2(e : mat2x2<S>) -> mat2x2<S>
파라미터화 Tf16 또는 f32
SAbstractFloat, f16, 또는 f32입니다.
설명 2x2 컬럼-주 행렬 matrix 생성자입니다.

TS와 다르면 변환이 발생합니다.

오버로드
@const @must_use fn mat2x2<T>(v1 : vec2<T>, v2 : vec2<T>) -> mat2x2<T>
@const @must_use fn mat2x2(v1 : vec2<T>, v2 : vec2<T>) -> mat2x2<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 2x2 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat2x2<T>(e1 : T, e2 : T, e3 : T, e4 : T) -> mat2x2<T>
@const @must_use fn mat2x2(e1 : T, e2 : T, e3 : T, e4 : T) -> mat2x2<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 2x2 컬럼-주 행렬 matrix를 생성합니다.

mat2x2(vec2(e1,e2), vec2(e3,e4))와 동일합니다.

17.1.2.7. mat2x3
오버로드
@const @must_use fn mat2x3<T>(e : mat2x3<S>) -> mat2x3<T>
@const @must_use fn mat2x3(e : mat2x3<S>) -> mat2x3<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 2x3 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat2x3<T>(v1 : vec3<T>, v2 : vec3<T>) -> mat2x3<T>
@const @must_use fn mat2x3(v1 : vec3<T>, v2 : vec3<T>) -> mat2x3<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 2x3 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat2x3<T>(e1 : T, ..., e6 : T) -> mat2x3<T>
@const @must_use fn mat2x3(e1 : T, ..., e6 : T) -> mat2x3<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 2x3 컬럼-주 행렬 matrix를 생성합니다.

mat2x3(vec3(e1,e2,e3), vec3(e4,e5,e6))와 동일합니다.

17.1.2.8. mat2x4
오버로드
@const @must_use fn mat2x4<T>(e : mat2x4<S>) -> mat2x4<T>
@const @must_use fn mat2x4(e : mat2x4<S>) -> mat2x4<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 2x4 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat2x4<T>(v1 : vec4<T>, v2 : vec4<T>) -> mat2x4<T>
@const @must_use fn mat2x4(v1 : vec4<T>, v2 : vec4<T>) -> mat2x4<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 2x4 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat2x4<T>(e1 : T, ..., e8 : T) -> mat2x4<T>
@const @must_use fn mat2x4(e1 : T, ..., e8 : T) -> mat2x4<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 2x4 컬럼-주 행렬 matrix를 생성합니다.

mat2x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8))와 동일합니다.

17.1.2.9. mat3x2
오버로드
@const @must_use fn mat3x2<T>(e : mat3x2<S>) -> mat3x2<T>
@const @must_use fn mat3x2(e : mat3x2<S>) -> mat3x2<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 3x2 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat3x2<T>(v1 : vec2<T>,
                              v2 : vec2<T>,
                              v3 : vec2<T>) -> mat3x2<T>
@const @must_use fn mat3x2(v1 : vec2<T>,
                           v2 : vec2<T>,
                           v3 : vec2<T>) -> mat3x2<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 3x2 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat3x2<T>(e1 : T, ..., e6 : T) -> mat3x2<T>
@const @must_use fn mat3x2(e1 : T, ..., e6 : T) -> mat3x2<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 3x2 컬럼-주 행렬 matrix를 생성합니다.

mat3x2(vec2(e1,e2), vec2(e3,e4), vec2(e5,e6))와 동일합니다.

17.1.2.10. mat3x3
오버로드
@const @must_use fn mat3x3<T>(e : mat3x3<S>) -> mat3x3<T>
@const @must_use fn mat3x3(e : mat3x3<S>) -> mat3x3<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 3x3 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat3x3<T>(v1 : vec3<T>,
                              v2 : vec3<T>,
                              v3 : vec3<T>) -> mat3x3<T>
@const @must_use fn mat3x3(v1 : vec3<T>,
                           v2 : vec3<T>,
                           v3 : vec3<T>) -> mat3x3<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 3x3 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat3x3<T>(e1 : T, ..., e9 : T) -> mat3x3<T>
@const @must_use fn mat3x3(e1 : T, ..., e9 : T) -> mat3x3<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 3x3 컬럼-주 행렬 matrix를 생성합니다.

mat3x3(vec3(e1,e2,e3), vec3(e4,e5,e6), vec3(e7,e8,e9))와 동일합니다.

17.1.2.11. mat3x4
오버로드
@const @must_use fn mat3x4<T>(e : mat3x4<S>) -> mat3x4<T>
@const @must_use fn mat3x4(e : mat3x4<S>) -> mat3x4<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 3x4 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat3x4<T>(v1 : vec4<T>,
                              v2 : vec4<T>,
                              v3 : vec4<T>) -> mat3x4<T>
@const @must_use fn mat3x4(v1 : vec4<T>,
                           v2 : vec4<T>,
                           v3 : vec4<T>) -> mat3x4<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 3x4 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat3x4<T>(e1 : T, ..., e12 : T) -> mat3x4<T>
@const @must_use fn mat3x4(e1 : T, ..., e12 : T) -> mat3x4<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 3x4 컬럼-주 행렬 matrix를 생성합니다.

mat3x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8), vec4(e9,e10,e11,e12))와 동일합니다.

17.1.2.12. mat4x2
오버로드
@const @must_use fn mat4x2<T>(e : mat4x2<S>) -> mat4x2<T>
@const @must_use fn mat4x2(e : mat4x2<S>) -> mat4x2<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 4x2 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat4x2<T>(v1 : vec2<T>,
                              v2 : vec2<T>,
                              v3 : vec2<T>,
                              v4: vec2<T>) -> mat4x2<T>
@const @must_use fn mat4x2(v1 : vec2<T>,
                           v2 : vec2<T>,
                           v3 : vec2<T>,
                           v4: vec2<T>) -> mat4x2<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 4x2 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat4x2<T>(e1 : T, ..., e8 : T) -> mat4x2<T>
@const @must_use fn mat4x2(e1 : T, ..., e8 : T) -> mat4x2<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 4x2 컬럼-주 행렬 matrix를 생성합니다.

mat4x2(vec2(e1,e2), vec2(e3,e4), vec2(e5,e6), vec2(e7,e8))와 동일합니다.

17.1.2.13. mat4x3
오버로드
@const @must_use fn mat4x3<T>(e : mat4x3<S>) -> mat4x3<T>
@const @must_use fn mat4x3(e : mat4x3<S>) -> mat4x3<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 4x3 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat4x3<T>(v1 : vec3<T>,
                              v2 : vec3<T>,
                              v3 : vec3<T>,
                              v4 : vec3<T>) -> mat4x3<T>
@const @must_use fn mat4x3(v1 : vec3<T>,
                           v2 : vec3<T>,
                           v3 : vec3<T>,
                           v4 : vec3<T>) -> mat4x3<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 4x3 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat4x3<T>(e1 : T, ..., e12 : T) -> mat4x3<T>
@const @must_use fn mat4x3(e1 : T, ..., e12 : T) -> mat4x3<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 4x3 컬럼-주 행렬 matrix를 생성합니다.

mat4x3(vec3(e1,e2,e3), vec3(e4,e5,e6), vec3(e7,e8,e9), vec3(e10,e11,e12))와 동일합니다.

17.1.2.14. mat4x4
오버로드
@const @must_use fn mat4x4<T>(e : mat4x4<S>) -> mat4x4<T>
@const @must_use fn mat4x4(e : mat4x4<S>) -> mat4x4<S>
파라미터화 Tf16 또는 f32입니다.
SAbstractFloat, f16, 또는 f32입니다.
설명 4x4 컬럼-주 행렬 matrix 생성자입니다.

TS가 일치하지 않으면 변환이 발생합니다.

오버로드
@const @must_use fn mat4x4<T>(v1 : vec4<T>,
                              v2 : vec4<T>,
                              v3 : vec4<T>,
                              v4 : vec4<T>) -> mat4x4<T>
@const @must_use fn mat4x4(v1 : vec4<T>,
                           v2 : vec4<T>,
                           v3 : vec4<T>,
                           v4 : vec4<T>) -> mat4x4<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 컬럼 벡터로부터 4x4 컬럼-주 행렬 matrix를 생성합니다.
오버로드
@const @must_use fn mat4x4<T>(e1 : T, ..., e16 : T) -> mat4x4<T>
@const @must_use fn mat4x4(e1 : T, ..., e16 : T) -> mat4x4<T>
파라미터화 TAbstractFloat, f16, 또는 f32입니다.
설명 구성 요소로부터 4x4 컬럼-주 행렬 matrix를 생성합니다.

mat4x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8), vec4(e9,e10,e11,e12), vec4(e13,e14,e15,e16))와 동일합니다.

17.1.2.15. 구조체
오버로드
@const @must_use fn S(e1 : T1, ..., eN : TN) -> S
파라미터화 S생성 가능한 구조체 타입이며, 멤버 타입은 T1 ... TN입니다.
설명 멤버로부터 S 타입의 구조체를 생성합니다.
17.1.2.16. u32
오버로드
@const @must_use fn u32(e : T) -> u32
파라미터화 T스칼라 타입입니다.
설명 u32 값을 생성합니다.

Tu32이면, 항등 연산입니다.
Ti32이면, 비트 재해석(즉, u32에서 e와 동일한 비트 패턴을 가지는 유일한 값이 결과)입니다.
T부동소수점 타입이면, eu32로 변환되며, 0 방향 반올림됩니다.
Tbool이면, etrue일 때 1u, 그렇지 않으면 0u가 결과입니다.
TAbstractInt이면, eu32로 표현 가능하면 항등 연산, 불가능하면 셰이더 생성 오류를 발생합니다.

참고: AbstractInt에서의 오버로드는 u32(4*1000*1000*1000)와 같은 표현식이, i32 타입에서는 오버플로우가 발생할 u32 값을 생성할 수 있도록 존재합니다. 만약 이 오버로드가 없다면, 오버로드 결정u32(i32) 오버로드를 선택하게 되고, AbstractInt 표현식이 자동으로 i32로 변환되어 오버플로우로 인해 셰이더 생성 오류가 발생합니다.

17.1.2.17. vec2
오버로드
@const @must_use fn vec2<T>(e : T) -> vec2<T>
@const @must_use fn vec2(e : S) -> vec2<S>
파라미터화 T구체적 스칼라이고,
S스칼라입니다.
설명 두 컴포넌트 벡터e로 모두 채워서 생성합니다.
오버로드
@const @must_use fn vec2<T>(e : vec2<S>) -> vec2<T>
@const @must_use fn vec2(e : vec2<S>) -> vec2<S>
파라미터화 T구체적 스칼라이고,
S스칼라입니다.
설명 컴포넌트별두 컴포넌트 벡터e.xe.y로 생성합니다.

TS와 다르면 변환되어 T(e.x)T(e.y)가 각 컴포넌트가 됩니다.

오버로드
@const @must_use fn vec2<T>(e1 : T, e2 : T) -> vec2<T>
@const @must_use fn vec2(e1 : T, e2 : T) -> vec2<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별두 컴포넌트 벡터e1e2로 생성합니다.
오버로드
@const @must_use fn vec2() -> vec2<T>
파라미터화 T는 AbstractInt입니다.
설명 vec2(0,0) 값을 반환합니다.
17.1.2.18. vec3
오버로드
@const @must_use fn vec3<T>(e : T) -> vec3<T>
@const @must_use fn vec3(e : S) -> vec3<S>
파라미터화 T구체적 스칼라입니다.
S스칼라입니다.
설명 세 컴포넌트 벡터e로 모두 채워서 생성합니다.
오버로드
@const @must_use fn vec3<T>(e : vec3<S>) -> vec3<T>
@const @must_use fn vec3(e : vec3<S>) -> vec3<S>
파라미터화 T구체적 스칼라입니다.
S스칼라입니다.
설명 컴포넌트별로 세 컴포넌트 벡터e.x, e.y, e.z로 생성합니다.

TS와 다르면 변환되어 T(e.x), T(e.y), T(e.z)가 각 컴포넌트가 됩니다.

오버로드
@const @must_use fn vec3<T>(e1 : T, e2 : T, e3 : T) -> vec3<T>
@const @must_use fn vec3(e1 : T, e2 : T, e3 : T) -> vec3<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 세 컴포넌트 벡터e1, e2, e3로 생성합니다.
오버로드
@const @must_use fn vec3<T>(v1 : vec2<T>, e1 : T) -> vec3<T>
@const @must_use fn vec3(v1 : vec2<T>, e1 : T) -> vec3<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 세 컴포넌트 벡터v1.x, v1.y, e1로 생성합니다.
오버로드
@const @must_use fn vec3<T>(e1 : T, v1 : vec2<T>) -> vec3<T>
@const @must_use fn vec3(e1 : T, v1 : vec2<T>) -> vec3<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 세 컴포넌트 벡터e1, v1.x, v1.y로 생성합니다.
오버로드
@const @must_use fn vec3() -> vec3<T>
파라미터화 T는 AbstractInt입니다.
설명 vec3(0,0,0) 값을 반환합니다.
17.1.2.19. vec4
오버로드
@const @must_use fn vec4<T>(e : T) -> vec4<T>
@const @must_use fn vec4(e : S) -> vec4<S>
파라미터화 T구체적 스칼라입니다.
S스칼라입니다.
설명 네 컴포넌트 벡터e로 모두 채워서 생성합니다.
오버로드
@const @must_use fn vec4<T>(e : vec4<S>) -> vec4<T>
@const @must_use fn vec4(e : vec4<S>) -> vec4<S>
파라미터화 T구체적 스칼라입니다.
S스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터e.x, e.y, e.z, e.w로 생성합니다.

TS와 다르면 변환되어 T(e.x), T(e.y), T(e.z), T(e.w)가 각 컴포넌트가 됩니다.

오버로드
@const @must_use fn vec4<T>(e1 : T, e2 : T, e3 : T, e4 : T) -> vec4<T>
@const @must_use fn vec4(e1 : T, e2 : T, e3 : T, e4 : T) -> vec4<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터e1, e2, e3, e4로 생성합니다.
오버로드
@const @must_use fn vec4<T>(e1 : T, v1 : vec2<T>, e2 : T) -> vec4<T>
@const @must_use fn vec4(e1 : T, v1 : vec2<T>, e2 : T) -> vec4<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터e1, v1.x, v1.y, e2로 생성합니다.
오버로드
@const @must_use fn vec4<T>(e1 : T, e2 : T, v1 : vec2<T>) -> vec4<T>
@const @must_use fn vec4(e1 : T, e2 : T, v1 : vec2<T>) -> vec4<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터e1, e2, v1.x, v1.y로 생성합니다.
오버로드
@const @must_use fn vec4<T>(v1 : vec2<T>, v2 : vec2<T>) -> vec4<T>
@const @must_use fn vec4(v1 : vec2<T>, v2 : vec2<T>) -> vec4<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터v1.x, v1.y, v2.x, v2.y로 생성합니다.
오버로드
@const @must_use fn vec4<T>(v1 : vec2<T>, e1 : T, e2 : T) -> vec4<T>
@const @must_use fn vec4(v1 : vec2<T>, e1 : T, e2 : T) -> vec4<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터v1.x, v1.y, e1, e2로 생성합니다.
오버로드
@const @must_use fn vec4<T>(v1 : vec3<T>, e1 : T) -> vec4<T>
@const @must_use fn vec4(v1 : vec3<T>, e1 : T) -> vec4<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별로 네 컴포넌트 벡터v1.x, v1.y, v1.z, e1로 생성합니다.
오버로드
@const @must_use fn vec4() -> vec4<T>
파라미터화 T는 AbstractInt입니다.
설명 vec4(0,0,0,0) 값을 반환합니다.

17.2. 비트 재해석 내장 함수

17.2.1. bitcast

bitcast 내장 함수는 한 타입의 값의 비트 표현을 다른 타입의 값으로 재해석할 때 사용됩니다.

내부 레이아웃 규칙은 § 14.4.4 값의 내부 레이아웃에 설명되어 있습니다.

오버로드
@const @must_use fn bitcast<T>(e : T) -> T
파라미터화 T구체적 수치 스칼라 또는 구체적 수치 벡터입니다.
설명 항등 변환.
컴포넌트별 변환은 T벡터일 때 적용됩니다.
결과는 e입니다.
오버로드
@const @must_use fn bitcast<T>(e : S) -> T
파라미터화 S는 i32, u32, 또는 f32
TS가 아니고 i32, u32, 또는 f32입니다.
설명 비트를 T로 재해석.
결과는 e의 비트를 T 값으로 재해석한 것입니다.
오버로드
@const @must_use fn bitcast<vecN<T>>(e : vecN<S>) -> vecN<T>
파라미터화 S는 i32, u32, 또는 f32
TS가 아니고 i32, u32, 또는 f32입니다.
설명 컴포넌트별로 비트를 T로 재해석.
결과는 e의 비트를 vecN<T> 값으로 재해석한 것입니다.
오버로드
@const @must_use fn bitcast<u32>(e : AbstractInt) -> u32
@const @must_use fn bitcast<vecN<u32>>(e : vecN<AbstractInt>) -> vecN<u32>
파라미터화
설명 eu32로 표현 가능하면 항등 연산, 불가능하면 셰이더 생성 오류를 발생합니다. 즉, u32(e)와 동일한 결과를 반환합니다.

e가 벡터일 때 컴포넌트별 연산입니다.

오버로드
@const @must_use fn bitcast<T>(e : vec2<f16>) -> T
파라미터화 T는 i32, u32, 또는 f32입니다.
설명 컴포넌트별로 비트를 T로 재해석.
결과는 e의 32비트를 T 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
오버로드
@const @must_use fn bitcast<vec2<T>>(e : vec4<f16>) -> vec2<T>
파라미터화 T는 i32, u32, 또는 f32입니다.
설명 컴포넌트별로 비트를 T로 재해석.
결과는 e의 64비트를 T 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
오버로드
@const @must_use fn bitcast<vec2<f16>>(e : T) -> vec2<f16>
파라미터화 T는 i32, u32, 또는 f32입니다.
설명 컴포넌트별로 비트를 f16으로 재해석.
결과는 e의 32비트를 f16 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
오버로드
@const @must_use fn bitcast<vec4<f16>>(e : vec2<T>) -> vec4<f16>
파라미터화 T는 i32, u32, 또는 f32입니다.
설명 컴포넌트별vec2<f16>로 비트를 재해석.
결과는 e의 64비트를 f16 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.

17.3. 논리 내장 함수

17.3.1. all

오버로드
@const @must_use fn all(e: vecN<bool>) -> bool
설명 e의 모든 컴포넌트가 true일 때 true를 반환합니다.
오버로드
@const @must_use fn all(e: bool) -> bool
설명 e를 반환합니다.

17.3.2. any

오버로드
@const @must_use fn any(e: vecN<bool>) -> bool
설명 e의 어떤 컴포넌트라도 true이면 true를 반환합니다.
오버로드
@const @must_use fn any(e: bool) -> bool
설명 e를 반환합니다.

17.3.3. select

오버로드
@const @must_use fn select(f: T,
                           t: T,
                           cond: bool) -> T
파라미터화 T스칼라 또는 벡터입니다.
설명 cond가 true일 때 t를, 그렇지 않으면 f를 반환합니다.
오버로드
@const @must_use fn select(f: vecN<T>,
                           t: vecN<T>,
                           cond: vecN<bool>) -> vecN<T>
파라미터화 T스칼라입니다.
설명 컴포넌트별 선택. 결과 컴포넌트 iselect(f[i], t[i], cond[i])로 평가됩니다.

17.4. 배열 내장 함수

17.4.1. arrayLength

오버로드
@must_use fn arrayLength(p: ptr<storage, array<E>, AM>) -> u32
파라미터화 E런타임 크기 배열의 요소 타입이며,
액세스 모드 AM읽기 또는 읽기/쓰기입니다.
설명 NRuntime 값을 반환하며, 이는 런타임 크기 배열의 요소 개수입니다.

§ 13.3.4 버퍼 바인딩이 런타임 크기 배열의 요소 개수를 결정함을 참고하세요.

예시: 런타임 크기 배열의 요소 개수 얻기
struct PointLight {
  position : vec3f,
  color : vec3f,
}

struct LightStorage {
  pointCount : u32,
  point : array<PointLight>,
}

@group(0) @binding(1) var<storage> lights : LightStorage;

fn num_point_lights() -> u32 {
  return arrayLength( &lights.point );
}

17.5. 수치 내장 함수

17.5.1. abs

오버로드
@const @must_use fn abs(e: T ) -> T
파라미터화 S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 절대값입니다. 컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

e가 부동소수점 타입이면, 결과는 양의 부호 비트가 있는 e입니다. e가 부호 없는 정수 스칼라 타입이면, 결과는 e입니다. e가 부호 있는 정수 스칼라 타입이고 가장 큰 음수 값으로 평가된다면, 결과는 e입니다.

17.5.2. acos

오버로드
@const @must_use fn acos(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 역코사인(cos-1)의 주값(라디안)을 반환합니다.
즉, cos(x) = e인 0 ≤ x ≤ π인 x에 근사합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

스칼라 도메인 [−1, 1] 구간

17.5.3. acosh

오버로드
@const @must_use fn acosh(x: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 x의 역쌍곡 코사인(cosh-1)을 쌍곡각으로 반환합니다.
즉, cosh(a) = x인 0 ≤ a ≤ +∞인 a에 근사합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

스칼라 도메인 [1, +∞] 구간

17.5.4. asin

오버로드
@const @must_use fn asin(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 역사인(sin-1)의 주값(라디안)을 반환합니다.
즉, sin(x) = e인 -π/2 ≤ x ≤ π/2인 x에 근사합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

스칼라 도메인 [−1, 1] 구간

17.5.5. asinh

오버로드
@const @must_use fn asinh(y: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 y의 역쌍곡 사인(sinh-1)을 쌍곡각으로 반환합니다.
즉, sinh(y) = aa에 근사합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.6. atan

오버로드
@const @must_use fn atan(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 역탄젠트(tan-1)의 주값(라디안)을 반환합니다.
즉, tan(x) = e인 − π/2 ≤ x ≤ π/2인 x에 근사합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.7. atanh

오버로드
@const @must_use fn atanh(t: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 t의 역쌍곡 탄젠트(tanh-1)를 쌍곡각으로 반환합니다.
즉, tanh(a) = ta에 근사합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

스칼라 도메인 [−1, 1] 구간

17.5.8. atan2

오버로드
@const @must_use fn atan2(y: T,
                          x: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 y÷x의 탄젠트인 각도(라디안)를 [-π, π] 구간에서 반환합니다.

결과의 사분면은 yx의 부호에 따라 결정됩니다. 예를 들어, 함수는 다음과 같이 구현될 수 있습니다:

  • x > 0일 때 atan(y/x)

  • (x < 0)이고 (y > 0)일 때 atan(y/x) + π

  • (x < 0)이고 (y < 0)일 때 atan(y/x) - π

참고: 결과의 오차는 제한되지 않습니다:
  • abs(x)가 매우 작을 때, 예를 들어 해당 타입에서 서브노멀인 경우,

  • 원점 (x,y) = (0,0)에서, 또는

  • y가 서브노멀 또는 무한대일 때.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.9. ceil

오버로드
@const @must_use fn ceil(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e천장값을 반환합니다. 컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.10. clamp

오버로드
@const @must_use fn clamp(e: T,
                          low: T,
                          high: T) -> T
파라미터화 S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 값을 범위 내로 제한합니다.

T가 정수 타입일 경우 결과는 min(max(e, low), high)입니다.

T가 부동소수점 타입일 경우 결과는 min(max(e, low), high) 또는 e, low, high 세 값의 중앙 값입니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

lowhigh보다 크면:

17.5.11. cos

오버로드
@const @must_use fn cos(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 코사인 값을 반환합니다. e는 라디안 단위입니다. 컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.
스칼라 도메인 구간 (−∞, +∞)

17.5.12. cosh

오버로드
@const @must_use fn cosh(a: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 a의 쌍곡 코사인 값을 반환합니다. a쌍곡각입니다. 순수 수학 함수 (ea + e−a)÷2에 근사하지만, 반드시 그렇게 계산되는 것은 아닙니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.13. countLeadingZeros

오버로드
@const @must_use fn countLeadingZeros(e: T) -> T
파라미터화 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
설명 스칼라 타입일 때 e의 최상위 비트부터 연속된 0 비트의 개수를 반환합니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.
일부 언어에서는 "clz"로도 알려져 있습니다.

17.5.14. countOneBits

오버로드
@const @must_use fn countOneBits(e: T) -> T
파라미터화 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
설명 e의 표현에서 1 비트의 개수를 반환합니다.
"population count"로도 알려져 있습니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.15. countTrailingZeros

오버로드
@const @must_use fn countTrailingZeros(e: T) -> T
파라미터화 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
설명 스칼라 타입일 때 e의 최하위 비트부터 연속된 0 비트의 개수를 반환합니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.
일부 언어에서는 "ctz"로도 알려져 있습니다.

17.5.16. cross

오버로드
@const @must_use fn cross(a: vec3<T>,
                          b: vec3<T>) -> vec3<T>
파라미터화 T는 AbstractFloat, f32, 또는 f16입니다.
설명 e1e2의 외적을 반환합니다.
도메인 선형 항으로부터 암시됨. 가능한 구현 예시:
  • a[1] × b[2] − a[2] × b[1]

  • a[2] × b[0] − a[0] × b[2]

  • a[0] × b[1] − a[1] × b[0]

17.5.17. degrees

오버로드
@const @must_use fn degrees(e1: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 라디안을 도로 변환합니다. e1 × 180 ÷ π에 근사합니다. 컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.18. determinant

오버로드
@const @must_use fn determinant(e: matCxC<T>) -> T
파라미터화 T는 AbstractFloat, f32, 또는 f16입니다.
설명 e의 행렬식 값을 반환합니다.
도메인 선형 항으로부터 암시됨. 행렬식의 표준 수학적 정의에 따릅니다.

17.5.19. distance

오버로드
@const @must_use fn distance(e1: T,
                             e2: T) -> S
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e1e2 사이의 거리를 반환합니다 (예: length(e1 - e2)).

도메인은 벡터 (e1,e2)의 뺄셈 e1e2가 유효한 모든 경우입니다. 즉, e1[i]e2[i]가 일부 컴포넌트 i에서 동일한 무한대 값인 경우를 제외한 모든 벡터 집합입니다.

17.5.20. dot

오버로드
@const @must_use fn dot(e1: vecN<T>,
                        e2: vecN<T>) -> T
파라미터화 T는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16입니다.
설명 e1e2의 내적을 반환합니다.
도메인 선형 항으로부터 암시됨. 각 항 e1[i] × e2[i]의 합계에 해당합니다.

17.5.21. dot4U8Packed

오버로드
@const @must_use fn dot4U8Packed(e1: u32,
                                 e2: u32) -> u32
설명 e1e2는 각각 네 개의 8비트 부호 없는 정수 컴포넌트로 이루어진 벡터로 해석됩니다. 이 두 벡터의 부호 없는 정수 내적을 반환합니다.

17.5.22. dot4I8Packed

오버로드
@const @must_use fn dot4I8Packed(e1: u32,
                                 e2: u32) -> i32
설명 e1e2는 각각 네 개의 8비트 부호 있는 정수 컴포넌트로 이루어진 벡터로 해석됩니다. 이 두 벡터의 부호 있는 정수 내적을 반환합니다. 각 컴포넌트는 곱셈을 수행하기 전에 i32로 부호 확장되며, 이후 덧셈 연산은 WGSL의 i32로 수행됩니다(덧셈 결과는 수학적으로 -65024에서 65536 범위 내에 있으므로 i32 범위 내에서 오버플로우가 발생하지 않습니다).

17.5.23. exp

오버로드
@const @must_use fn exp(e1: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e1의 자연 지수를 반환합니다(예: ee1). 컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.24. exp2

오버로드
@const @must_use fn exp2(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 2를 e의 거듭제곱한 값을 반환합니다(예: 2e). 컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.25. extractBits (signed)

오버로드
@const @must_use fn extractBits(e: T,
                                offset: u32,
                                count: u32) -> T
파라미터화 T는 i32 또는 vecN<i32>입니다.
설명 정수에서 비트를 읽어오며, 부호 확장이 포함됩니다.

T가 스칼라 타입일 때:

  • wT의 비트 폭입니다.
  • o = min(offset, w)
  • c = min(count, w - o)
  • c가 0이면 결과는 0입니다.
  • 그 외에는 결과의 0..c - 1 비트가 eo..o + c - 1 비트에서 복사됩니다. 결과의 나머지 비트는 결과의 c - 1 비트와 동일합니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

count + offsetw보다 크면:

17.5.26. extractBits (unsigned)

오버로드
@const @must_use fn extractBits(e: T,
                                offset: u32,
                                count: u32) -> T
파라미터화 T는 u32 또는 vecN<u32>입니다.
설명 정수에서 비트를 읽어오며, 부호 확장은 없습니다.

T가 스칼라 타입일 때:

  • wT의 비트 폭입니다.
  • o = min(offset, w)
  • c = min(count, w - o)
  • c가 0이면 결과는 0입니다.
  • 그 외에는 결과의 0..c - 1 비트가 eo..o + c - 1 비트에서 복사됩니다. 결과의 나머지 비트는 0입니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

count + offsetw보다 크면:

17.5.27. faceForward

오버로드
@const @must_use fn faceForward(e1: T,
                                e2: T,
                                e3: T) -> T
파라미터화 T는 vecN<AbstractFloat>, vecN<f32>, 또는 vecN<f16>입니다.
설명 dot(e2, e3)가 음수이면 e1을, 그렇지 않으면 -e1을 반환합니다.
도메인 도메인 제한은 dot(e2,e3) 연산에서 발생합니다: 선형 항으로부터 암시됨. 각 항 e2[i] × e3[i]의 합에 해당합니다.

17.5.28. firstLeadingBit (signed)

오버로드
@const @must_use fn firstLeadingBit(e: T) -> T
파라미터화 T는 i32 또는 vecN<i32>입니다.
설명 스칼라 T일 때 결과는 다음과 같습니다:
  • 만약 e가 0 또는 -1이면 -1을 반환합니다.
  • 그 외에는 e의 부호 비트와 다른 가장 높은 비트의 위치를 반환합니다.

컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

참고: 부호 있는 정수는 2의 보수 표현을 사용하므로, 부호 비트가 가장 높은 비트 위치에 나타납니다.

17.5.29. firstLeadingBit (unsigned)

오버로드
@const @must_use fn firstLeadingBit(e: T) -> T
파라미터화 T는 u32 또는 vecN<u32>입니다.
설명 스칼라 T일 때 결과는 다음과 같습니다:
  • e가 0이면 T(-1)을 반환합니다.
  • 그 외에는 e에서 가장 높은 1 비트의 위치를 반환합니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.30. firstTrailingBit

오버로드
@const @must_use fn firstTrailingBit(e: T) -> T
파라미터화 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
설명 스칼라 T일 때 결과는 다음과 같습니다:
  • e가 0이면 T(-1)을 반환합니다.
  • 그 외에는 e에서 가장 낮은 1 비트의 위치를 반환합니다.
컴포넌트 기준으로 T가 벡터인 경우 적용됩니다.

17.5.31. floor

오버로드
@const @must_use fn floor(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e내림값을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.

17.5.32. fma

오버로드
@const @must_use fn fma(e1: T,
                        e2: T,
                        e3: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e1 * e2 + e3 값을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.

참고: fma는 "fused multiply add"의 줄임말입니다.

참고: IEEE-754 fusedMultiplyAdd 연산은 중간 결과를 무한대 범위와 정밀도로 계산한 것처럼 수행하며, 최종 결과만 반올림 되어 대상 타입에 맞춰집니다. 하지만 § 15.7.4 부동소수점 정확도fma 규칙에서는 곱셈과 덧셈 모두 일반적인 방식으로 수행하는 구현도 허용합니다. 이 경우 중간 결과 값이 오버플로우되거나 정확도가 손실될 수 있으며, 전체 연산이 "퓨즈드"되지 않습니다.

도메인 선형 항에서 암시됨(식 e2 × e2 + e3).

17.5.33. fract

오버로드
@const @must_use fn fract(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 소수 부분(e - floor(e))을 반환합니다.
컴포넌트별T가 벡터인 경우 적용됩니다.

참고: 유효 결과는 [0, 1.0] 구간에 있습니다. 예를 들어 e가 매우 작은 음수일 경우 fract(e)는 1.0이 될 수 있습니다.

17.5.34. frexp

오버로드
@const @must_use fn frexp(e: T) -> __frexp_result_f32
파라미터화 T는 f32입니다.
설명 e를 소수 부분과 지수로 분리합니다.
  • e가 0일 때 소수 부분은 0입니다.

  • e가 0이 아니고 정상(normal)일 때, e = fraction * 2exponent, 단 fraction은 [0.5, 1.0) 또는 (-1.0, -0.5] 범위입니다.

  • 그 외에는 e서브노멀이거나 NaN, 무한대입니다. 결과 소수와 지수는 불확정 값입니다.

__frexp_result_f32 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __frexp_result_f32 {
  fract : f32, // 소수 부분
  exp : i32    // 지수 부분
}

참고: frexp의 뜻은 "fraction and exponent"입니다.

예시: frexp 사용
// 결과 타입 추론
let fraction_and_exponent = frexp(1.5);
// fraction_only는 0.75가 됨
let fraction_only = frexp(1.5).fract;

참고: __frexp_result_f32 타입으로 값을 명시적으로 선언할 수는 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn frexp(e: T) -> __frexp_result_f16
파라미터화 T는 f16입니다.
설명 e를 소수 부분과 지수로 분리합니다.
  • e가 0일 때 소수 부분은 0입니다.

  • e가 0이 아니고 정상(normal)일 때, e = fraction * 2exponent, 단 fraction은 [0.5, 1.0) 또는 (-1.0, -0.5] 범위입니다.

  • 그 외에는 e서브노멀이거나 NaN, 무한대입니다. 결과 소수와 지수는 불확정 값입니다.

__frexp_result_f16 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __frexp_result_f16 {
  fract : f16, // 소수 부분
  exp : i32    // 지수 부분
}

참고: frexp의 뜻은 "fraction and exponent"입니다.

참고: __frexp_result_f16 타입으로 값을 명시적으로 선언할 수는 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn frexp(e: T) -> __frexp_result_abstract
파라미터화 T는 AbstractFloat입니다.
설명 e를 소수 부분과 지수로 분리합니다.
  • e가 0일 때 소수 부분은 0입니다.

  • e가 0이 아니고 정상(normal)일 때, e = fraction * 2exponent, 단 fraction은 [0.5, 1.0) 또는 (-1.0, -0.5] 범위입니다.

  • e서브노멀일 경우, fraction과 exponent는 무한대 오차를 가집니다. fraction은 임의의 AbstractFloat 값이 될 수 있고, exponent는 임의의 AbstractInt 값이 될 수 있습니다.

참고: AbstractFloat 연산에서 무한대 또는 NaN이 발생하면 셰이더 생성 오류가 발생합니다.

__frexp_result_abstract 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __frexp_result_abstract {
  fract : AbstractFloat, // 소수 부분
  exp : AbstractInt      // 지수 부분
}

참고: frexp의 뜻은 "fraction and exponent"입니다.

예시: abstract frexp 사용
// 결과 타입 추론
const fraction_and_exponent = frexp(1.5);
// fraction_only는 0.75가 됨
const fraction_only = frexp(1.5).fract;

참고: __frexp_result_abstract 타입으로 값을 명시적으로 선언할 수는 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn frexp(e: T) -> __frexp_result_vecN_f32
파라미터화 T는 vecN<f32>입니다.
설명 e의 각 요소(ei)를 소수 부분과 지수로 분리합니다.
  • ei가 0일 때 소수 부분은 0입니다.

  • ei가 0이 아니고 정상(normal)일 때, ei = fraction * 2exponent, 단 fraction은 [0.5, 1.0) 또는 (-1.0, -0.5] 범위입니다.

  • 그 외에는 ei가 NaN 또는 무한대입니다. 결과 소수와 지수는 불확정 값입니다.

__frexp_result_vecN_f32 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __frexp_result_vecN_f32 {
  fract : vecN<f32>, // 소수 부분
  exp : vecN<i32>    // 지수 부분
}

참고: frexp의 뜻은 "fraction and exponent"입니다.

참고: __frexp_result_vecN_f32 타입으로 값을 명시적으로 선언할 수는 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn frexp(e: T) -> __frexp_result_vecN_f16
파라미터화 T는 vecN<f16>입니다.
설명 e의 각 요소(ei)를 소수 부분과 지수로 분리합니다.
  • ei가 0일 때 소수 부분은 0입니다.

  • ei가 0이 아니고 정상(normal)일 때, ei = fraction * 2exponent, 단 fraction은 [0.5, 1.0) 또는 (-1.0, -0.5] 범위입니다.

  • 그 외에는 ei가 NaN 또는 무한대입니다. 결과 소수와 지수는 불확정 값입니다.

__frexp_result_vecN_f16 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __frexp_result_vecN_f16 {
  fract : vecN<f16>, // 소수 부분
  exp : vecN<i32>    // 지수 부분
}

참고: frexp의 뜻은 "fraction and exponent"입니다.

참고: __frexp_result_vecN_f16 타입으로 값을 명시적으로 선언할 수는 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn frexp(e: T) -> __frexp_result_vecN_abstract
파라미터화 T는 vecN<AbstractFloat>입니다.
설명 e의 각 요소(ei)를 소수 부분과 지수로 분리합니다.
  • ei가 0일 때 소수 부분은 0입니다.

  • ei가 0이 아니고 정상(normal)일 때, ei = fraction * 2exponent, 단 fraction은 [0.5, 1.0) 또는 (-1.0, -0.5] 범위입니다.

  • ei서브노멀일 경우, fraction과 exponent는 무한대 오차를 가집니다. fraction은 임의의 AbstractFloat 값이 될 수 있고, exponent는 임의의 AbstractInt 값이 될 수 있습니다.

참고: AbstractFloat 연산에서 무한대 또는 NaN이 발생하면 셰이더 생성 오류가 발생합니다.

__frexp_result_vecN_abstract 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __frexp_result_vecN_abstract {
  fract : vecN<AbstractFloat>, // 소수 부분
  exp : vecN<AbstractInt>      // 지수 부분
}

참고: frexp의 뜻은 "fraction and exponent"입니다.

참고: __frexp_result_vecN_abstract 타입으로 값을 명시적으로 선언할 수는 없지만, 타입 추론은 가능합니다.

17.5.35. insertBits

오버로드
@const @must_use fn insertBits(e: T,
                              newbits: T,
                              offset: u32,
                              count: u32) -> T
파라미터화 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
설명 정수의 비트를 설정합니다.

T가 스칼라 타입일 경우:

  • wT의 비트 너비입니다.
  • o = min(offset, w)
  • c = min(count, w - o)
  • c가 0이면 결과는 e입니다.
  • 그렇지 않으면, 결과의 o..o + c - 1 비트는 newbits0..c - 1 비트에서 복사합니다. 나머지 비트는 e에서 복사합니다.
컴포넌트별T가 벡터인 경우 적용됩니다.

count + offsetw보다 크면:

17.5.36. inverseSqrt

오버로드
@const @must_use fn inverseSqrt(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 sqrt(e)의 역수를 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.
스칼라 도메인 구간 [0, +∞]

17.5.37. ldexp

오버로드
@const @must_use fn ldexp(e1: T,
                          e2: I) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
I는 AbstractInt, i32, vecN<AbstractInt>, 또는 vecN<i32>
I가 벡터일 때만 T도 벡터
T추상 타입일 수 있는 경우는 I추상 타입인 경우뿐이며 그 반대도 동일함

참고: 하나의 파라미터가 구체 타입이면 다른 파라미터도 자동 변환을 거쳐 구체 타입이 되고, 결과도 구체 타입이 됩니다.

설명 e1 * 2e2를 반환합니다. 단, 예외:

여기서 bias는 부동소수점 포맷의 지수 바이어스입니다:

x가 해당 타입에서 0 또는 정상적인 유한 값이면:

x = ldexp(frexp(x).fract, frexp(x).exp)

컴포넌트별T가 벡터인 경우 적용됩니다.

참고: ldexp는 "load exponent"의 약자입니다. 이 이름은 PDP-11의 부동소수점 유닛의 명령어에서 따온 것일 수 있습니다.

17.5.38. length

오버로드
@const @must_use fn length(e: T) -> S
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 길이를 반환합니다.
T스칼라일 경우 절댓값으로 평가됩니다.
T가 벡터 타입일 경우 sqrt(e[0]2 + e[1]2 + ...)로 평가됩니다.

참고: 스칼라의 경우 sqrt(e * e)로 평가될 수 있는데, 이는 불필요한 오버플로우 또는 정확도 손실을 초래할 수 있습니다.

17.5.39. log

오버로드
@const @must_use fn log(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 자연 로그 값을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.
스칼라 도메인 구간 [0, +∞]

17.5.40. log2

오버로드
@const @must_use fn log2(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>입니다.
설명 e의 2진 로그 값을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.
스칼라 도메인 구간 [0, +∞]

17.5.41. max

오버로드
@const @must_use fn max(e1: T,
                        e2: T) -> T
파라미터화 S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e1e2보다 작으면 e2를 반환하고, 그렇지 않으면 e1을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.

e1e2가 부동소수점 값인 경우:

  • e1e2 모두 서브노멀이면, 결과는 두 값 중 어느 것이든 될 수 있습니다.

17.5.42. min

오버로드
@const @must_use fn min(e1: T,
                        e2: T) -> T
파라미터화 S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e2e1보다 작으면 e2를, 그렇지 않으면 e1을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.

e1e2가 부동소수점 값인 경우:

  • e1e2 모두 서브노멀이면, 결과는 두 값 중 어느 것이든 될 수 있습니다.

17.5.43. mix

오버로드
@const @must_use fn mix(e1: T,
                        e2: T,
                        e3: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e1e2를 선형적으로 혼합한 값을 반환합니다(예: e1 * (T(1) - e3) + e2 * e3). 컴포넌트별T가 벡터인 경우 적용됩니다.
도메인 선형 항에서 암시됨: e1[i] × (1 − e3[i]) + e2[i] × e3[i]. e2[i] × e2[i] + e3[i].
오버로드
@const @must_use fn mix(e1: T2,
                        e2: T2,
                        e3: T) -> T2
파라미터화 T는 AbstractFloat, f32, 또는 f16
T2는 vecN<T>
설명 e1e2를 컴포넌트별로 선형적으로 혼합하며, 각 컴포넌트에 대해 스칼라 혼합 인자 e3를 적용합니다.
mix(e1, e2, T2(e3))와 동일합니다.
도메인 선형 항에서 암시됨: e1[i] × (1 − e3) + e2[i] × e3.

17.5.44. modf

오버로드
@const @must_use fn modf(e: T) -> __modf_result_f32
파라미터화 T는 f32
설명 e를 소수 부분과 정수 부분으로 분리합니다.

정수 부분은 trunc(e)이고, 소수 부분은 e - trunc(e)입니다.

__modf_result_f32 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __modf_result_f32 {
  fract : f32, // 소수 부분
  whole : f32  // 정수 부분
}
예시: modf 사용
// 결과 타입 추론
let fract_and_whole = modf(1.5);
// fract_only는 0.5가 됨
let fract_only = modf(1.5).fract;
// whole_only는 1.0이 됨
let whole_only = modf(1.5).whole;

참고: __modf_result_f32 타입으로 값을 명시적으로 선언할 수 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn modf(e: T) -> __modf_result_f16
파라미터화 T는 f16
설명 e를 소수 부분과 정수 부분으로 분리합니다.

정수 부분은 trunc(e)이고, 소수 부분은 e - trunc(e)입니다.

__modf_result_f16 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __modf_result_f16 {
  fract : f16, // 소수 부분
  whole : f16  // 정수 부분
}

참고: __modf_result_f16 타입으로 값을 명시적으로 선언할 수 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn modf(e: T) -> __modf_result_abstract
파라미터화 T는 AbstractFloat
설명 e를 소수 부분과 정수 부분으로 분리합니다.

정수 부분은 trunc(e)이고, 소수 부분은 e - trunc(e)입니다.

__modf_result_abstract 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __modf_result_abstract {
  fract : AbstractFloat, // 소수 부분
  whole : AbstractFloat  // 정수 부분
}
예시: modf abstract 사용
// 결과 타입 추론
const fract_and_whole = modf(1.5);
// fract_only는 0.5가 됨
const fract_only = modf(1.5).fract;
// whole_only는 1.0이 됨
const whole_only = modf(1.5).whole;

참고: __modf_result_abstract 타입으로 값을 명시적으로 선언할 수 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn modf(e: T) -> __modf_result_vecN_f32
파라미터화 T는 vecN<f32>
설명 e의 각 컴포넌트를 소수 부분과 정수 부분으로 분리합니다.

i번째 컴포넌트의 정수 및 소수 부분은 modf(e[i])의 결과와 같습니다.

__modf_result_vecN_f32 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __modf_result_vecN_f32 {
  fract : vecN<f32>, // 소수 부분
  whole : vecN<f32>  // 정수 부분
}

참고: __modf_result_vecN_f32 타입으로 값을 명시적으로 선언할 수 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn modf(e: T) -> __modf_result_vecN_f16
파라미터화 T는 vecN<f16>
설명 e의 각 컴포넌트를 소수 부분과 정수 부분으로 분리합니다.

i번째 컴포넌트의 정수 및 소수 부분은 modf(e[i])의 결과와 같습니다.

__modf_result_vecN_f16 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __modf_result_vecN_f16 {
  fract : vecN<f16>, // 소수 부분
  whole : vecN<f16>  // 정수 부분
}

참고: __modf_result_vecN_f16 타입으로 값을 명시적으로 선언할 수 없지만, 타입 추론은 가능합니다.

오버로드
@const @must_use fn modf(e: T) -> __modf_result_vecN_abstract
파라미터화 T는 vecN<AbstractFloat>
설명 e의 각 컴포넌트를 소수 부분과 정수 부분으로 분리합니다.

i번째 컴포넌트의 정수 및 소수 부분은 modf(e[i])의 결과와 같습니다.

__modf_result_vecN_abstract 내장 구조체를 반환합니다. 정의는 다음과 같습니다:

struct __modf_result_vecN_abstract {
  fract : vecN<AbstractFloat>, // 소수 부분
  whole : vecN<AbstractFloat>  // 정수 부분
}

참고: __modf_result_vecN_abstract 타입으로 값을 명시적으로 선언할 수 없지만, 타입 추론은 가능합니다.

17.5.45. normalize

오버로드
@const @must_use fn normalize(e: vecN<T> ) -> vecN<T>
파라미터화 T는 AbstractFloat, f32, 또는 f16입니다.
설명 e와 같은 방향의 단위 벡터를 반환합니다.

도메인은 영벡터를 제외한 모든 벡터입니다.

17.5.46. pow

오버로드
@const @must_use fn pow(e1: T,
                        e2: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e1e2의 거듭제곱한 값을 반환합니다. 컴포넌트별T가 벡터인 경우 적용됩니다.
스칼라 도메인 확장 실수 (x,y) 쌍 전체 집합에서, 다음을 제외합니다:
  • x < 0.

  • x가 1이고 y가 무한대일 때.

  • x가 무한대이고 y가 0일 때.

이 규칙은 결과가 exp2(y * log2(x))로 계산될 수 있다는 사실에서 비롯됩니다.

17.5.47. quantizeToF16

오버로드
@const @must_use fn quantizeToF16(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>입니다.
설명 32비트 부동소수점 값 eIEEE-754 binary16 값으로 변환한 뒤, 다시 IEEE-754 binary32 값으로 변환한 것과 같이 양자화합니다.

e가 binary16의 유한 범위를 벗어나면:

중간 binary16 값이 0으로 플러시될 수 있습니다. 즉, 중간 binary16 값이 서브노멀일 경우 최종 결과가 0이 될 수 있습니다.

§ 15.7.6 부동소수점 변환을 참고하십시오.

컴포넌트별T가 벡터일 때 적용됩니다.

참고: vec2<f32>의 경우 unpack2x16float(pack2x16float(e))와 동일합니다.

17.5.48. radians

오버로드
@const @must_use fn radians(e1: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 도를 라디안으로 변환합니다. e1 × π ÷ 180에 근사합니다. 컴포넌트별T가 벡터일 때 적용됩니다.

17.5.49. reflect

오버로드
@const @must_use fn reflect(e1: T,
                            e2: T) -> T
파라미터화 T는 vecN<AbstractFloat>, vecN<f32>, 또는 vecN<f16>입니다.
설명 입사 벡터 e1와 표면 방향 e2에 대해, 반사 방향 e1 - 2 * dot(e2, e1) * e2를 반환합니다.

17.5.50. refract

오버로드
@const @must_use fn refract(e1: T,
                            e2: T,
                            e3: I) -> T
파라미터화 T는 vecN<I>
I는 AbstractFloat, f32, 또는 f16
설명 입사 벡터 e1, 표면 법선 e2, 굴절률의 비 e3에 대하여, k = 1.0 - e3 * e3 * (1.0 - dot(e2, e1) * dot(e2, e1))로 계산합니다. k < 0.0이면 굴절 벡터 0.0을 반환하고, 그렇지 않으면 e3 * e1 - (e3 * dot(e2, e1) + sqrt(k)) * e2를 반환합니다. 입사 벡터 e1와 법선 e2는 스넬의 법칙에 따라 원하는 결과를 얻으려면 정규화되어야 하며, 그렇지 않으면 결과가 물리적으로 예상과 다를 수 있습니다.

17.5.51. reverseBits

오버로드
@const @must_use fn reverseBits(e: T) -> T
파라미터화 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
설명 e의 비트를 뒤집습니다: 결과의 k번째 비트는 e31 -k번째 비트와 같습니다.
컴포넌트별T가 벡터일 때 적용됩니다.

17.5.52. round

오버로드
@const @must_use fn round(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 결과는 e와 가장 가까운 정수 k를 부동소수점 값으로 반환합니다.
e가 정수 kk + 1 사이에 정확히 위치할 경우, k가 짝수면 k를, 홀수면 k + 1을 반환합니다.
컴포넌트별T가 벡터일 때 적용됩니다.

17.5.53. saturate

오버로드
@const @must_use fn saturate(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 clamp(e, 0.0, 1.0)을 반환합니다. 컴포넌트별T가 벡터일 때 적용됩니다.

17.5.54. sign

오버로드
@const @must_use fn sign(e: T) -> T
파라미터화 S는 AbstractInt, AbstractFloat, i32, f32, 또는 f16
T는 S 또는 vecN<S>
설명 결과는 다음과 같습니다:
  • e > 0이면 1
  • e = 0이면 0
  • e < 0이면 -1

컴포넌트별T가 벡터일 때 적용됩니다.

17.5.55. sin

오버로드
@const @must_use fn sin(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e의 사인 값을 반환합니다. e는 라디안 단위입니다. 컴포넌트별T가 벡터일 때 적용됩니다.
스칼라 도메인 구간 (−∞, +∞)

17.5.56. sinh

오버로드
@const @must_use fn sinh(a: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 a의 쌍곡 사인 값을 반환합니다. a쌍곡각입니다. 순수 수학 함수 (eae−a)÷2에 근사하지만, 반드시 그렇게 계산되는 것은 아닙니다.

컴포넌트별T가 벡터일 때 적용됩니다.

17.5.57. smoothstep

오버로드
@const @must_use fn smoothstep(edge0: T,
                               edge1: T,
                               x: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 0과 1 사이의 스무스 Hermite 보간 값을 반환합니다. 컴포넌트별T가 벡터일 때 적용됩니다.

스칼라 T의 경우 결과는 t * t * (3.0 - 2.0 * t),
여기서 t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)입니다.

질적으로 설명하면:

  • edge0 < edge1인 경우, 함수는 xedge0보다 작으면 0, 그 후 xedge1에 도달할 때까지 부드럽게 상승하고, 이후에는 1을 유지합니다.

  • edge0 > edge1인 경우, 함수는 xedge1보다 작으면 1, 그 후 xedge0에 도달할 때까지 부드럽게 하강하고, 이후에는 0을 유지합니다.

edge0 = edge1인 경우:

17.5.58. sqrt

오버로드
@const @must_use fn sqrt(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e의 제곱근 값을 반환합니다. 컴포넌트별T가 벡터일 때 적용됩니다.
스칼라 도메인 구간 [0, +∞]

17.5.59. step

오버로드
@const @must_use fn step(edge: T,
                         x: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 edgex이면 1.0을, 그렇지 않으면 0.0을 반환합니다. 컴포넌트별T가 벡터일 때 적용됩니다.

17.5.60. tan

오버로드
@const @must_use fn tan(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 e의 탄젠트 값을 반환합니다. e는 라디안 단위입니다. 컴포넌트별T가 벡터일 때 적용됩니다.
스칼라 도메인 구간 (−∞, +∞)

17.5.61. tanh

오버로드
@const @must_use fn tanh(a: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 a의 쌍곡 탄젠트 값을 반환합니다. a쌍곡각입니다. 순수 수학 함수 (eae−a) ÷ (ea + e−a) 에 근사하지만, 반드시 그렇게 계산되는 것은 아닙니다.

컴포넌트별T가 벡터일 때 적용됩니다.

17.5.62. transpose

오버로드
@const @must_use fn transpose(e: matRxC<T>) -> matCxR<T>
파라미터화 T는 AbstractFloat, f32, 또는 f16
설명 e의 전치 행렬을 반환합니다.

17.5.63. trunc

오버로드
@const @must_use fn trunc(e: T) -> T
파라미터화 S는 AbstractFloat, f32, 또는 f16
T는 S 또는 vecN<S>
설명 truncate(e) 값을 반환합니다. 이는 e의 절댓값보다 작거나 같은 가장 가까운 정수입니다. 컴포넌트별T가 벡터일 때 적용됩니다.

17.6. 미분 내장 함수

§ 15.6.2 미분을 참고하세요.

이 함수들의 호출은 다음을 의미합니다:

17.6.1. dpdx

오버로드
@must_use fn dpdx(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 윈도우 x 좌표에 대한 e의 편미분입니다. 결과는 dpdxFine(e) 또는 dpdxCoarse(e)와 동일합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.2. dpdxCoarse

오버로드
@must_use fn dpdxCoarse(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 윈도우 x 좌표에 대한 e의 편미분을 로컬 차분을 사용하여 반환합니다. 이는 dpdxFine(e)보다 더 적은 고유 위치를 반환할 수 있습니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.3. dpdxFine

오버로드
@must_use fn dpdxFine(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 윈도우 x 좌표에 대한 e의 편미분을 반환합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.4. dpdy

오버로드
@must_use fn dpdy(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 e의 윈도우 y 좌표에 대한 편미분입니다. 결과는 dpdyFine(e) 또는 dpdyCoarse(e)와 동일합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.5. dpdyCoarse

오버로드
@must_use fn dpdyCoarse(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 e의 윈도우 y 좌표에 대한 편미분을 로컬 차분을 사용하여 반환합니다. 이는 dpdyFine(e)보다 더 적은 고유 위치를 반환할 수 있습니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.6. dpdyFine

오버로드
@must_use fn dpdyFine(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 e의 윈도우 y 좌표에 대한 편미분을 반환합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.7. fwidth

오버로드
@must_use fn fwidth(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 abs(dpdx(e)) + abs(dpdy(e))를 반환합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.8. fwidthCoarse

오버로드
@must_use fn fwidthCoarse(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 abs(dpdxCoarse(e)) + abs(dpdyCoarse(e))를 반환합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.6.9. fwidthFine

오버로드
@must_use fn fwidthFine(e: T) -> T
파라미터화 T는 f32 또는 vecN<f32>
설명 abs(dpdxFine(e)) + abs(dpdyFine(e))를 반환합니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다.

17.7. 텍스처 내장 함수

파라미터 값은 각 텍스처 타입에 대해 유효해야 합니다.

17.7.1. textureDimensions

텍스처 또는 텍스처의 mip 레벨의 크기를 텍셀 단위로 반환합니다.

파라미터화 오버로드
STi32, u32, 또는 f32
F텍셀 포맷
A액세스 모드

Ttexture_1d<ST> 또는 texture_storage_1d<F,A>
@must_use fn textureDimensions(t: T) -> u32
STi32, u32, 또는 f32

Ttexture_1d<ST>

Li32 또는 u32

@must_use fn textureDimensions(t: T,
                               level: L) -> u32
STi32, u32, 또는 f32
F텍셀 포맷
A액세스 모드

Ttexture_2d<ST>, texture_2d_array<ST>, texture_cube<ST>, texture_cube_array<ST>, texture_multisampled_2d<ST>, texture_depth_2d, texture_depth_2d_array, texture_depth_cube, texture_depth_cube_array, texture_depth_multisampled_2d, texture_storage_2d<F,A>, texture_storage_2d_array<F,A>, 또는 texture_external
@must_use fn textureDimensions(t: T) -> vec2<u32>
STi32, u32, 또는 f32

Ttexture_2d<ST>, texture_2d_array<ST>, texture_cube<ST>, texture_cube_array<ST>, texture_depth_2d, texture_depth_2d_array, texture_depth_cube, 또는 texture_depth_cube_array

Li32 또는 u32

@must_use fn textureDimensions(t: T,
                               level: L) -> vec2<u32>
STi32, u32, 또는 f32
F텍셀 포맷
A액세스 모드

Ttexture_3d<ST> 또는 texture_storage_3d<F,A>
@must_use fn textureDimensions(t: T) -> vec3<u32>
STi32, u32, 또는 f32

Ttexture_3d<ST>

Li32 또는 u32

@must_use fn textureDimensions(t: T,
                               level: L) -> vec3<u32>

파라미터:

t 샘플링된, 멀티샘플링된, 깊이, 스토리지, 또는 외부 텍스처.
level mip 레벨이며, level 0은 텍스처의 전체 크기 버전을 포함합니다.
생략하면 level 0의 크기가 반환됩니다.

반환:

텍스처의 좌표 크기입니다.

즉, 결과는 논리 텍셀 주소의 좌표에 대한 정수 경계를 제공합니다. mip 레벨 수, 배열 크기, 샘플 수는 제외됩니다.

큐브 기반 텍스처의 경우 결과는 각 큐브 면의 크기입니다. 큐브 면은 정사각형이므로 결과의 x와 y 성분이 동일합니다.

level[0, textureNumLevels(t)) 범위를 벗어나면 반환 타입에 대해 불확정 값 이 반환될 수 있습니다.

17.7.2. textureGather

텍스처 갤러 연산은 2D, 2D 배열, 큐브 또는 큐브 배열 텍스처에서 읽기를 수행하며, 다음과 같이 네 성분 벡터를 계산합니다:

이 네 개의 텍셀은 WebGPU 샘플러 디스크립터에서 설명된 샘플링 영역을 구성합니다.

파라미터화 오버로드
Ci32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureGather(component: C,
                           t: texture_2d<ST>,
                           s: sampler,
                           coords: vec2<f32>) -> vec4<ST>
Ci32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureGather(component: C,
                           t: texture_2d<ST>,
                           s: sampler,
                           coords: vec2<f32>,
                           offset: vec2<i32>) -> vec4<ST>
Ci32 또는 u32
Ai32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureGather(component: C,
                           t: texture_2d_array<ST>,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A) -> vec4<ST>
Ci32 또는 u32
Ai32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureGather(component: C,
                           t: texture_2d_array<ST>,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A,
                           offset: vec2<i32>) -> vec4<ST>
Ci32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureGather(component: C,
                           t: texture_cube<ST>,
                           s: sampler,
                           coords: vec3<f32>) -> vec4<ST>
Ci32 또는 u32
Ai32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureGather(component: C,
                           t: texture_cube_array<ST>,
                           s: sampler,
                           coords: vec3<f32>,
                           array_index: A) -> vec4<ST>
@must_use fn textureGather(t: texture_depth_2d,
                           s: sampler,
                           coords: vec2<f32>) -> vec4<f32>
@must_use fn textureGather(t: texture_depth_2d,
                           s: sampler,
                           coords: vec2<f32>,
                           offset: vec2<i32>) -> vec4<f32>
@must_use fn textureGather(t: texture_depth_cube,
                           s: sampler,
                           coords: vec3<f32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureGather(t: texture_depth_2d_array,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureGather(t: texture_depth_2d_array,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A,
                           offset: vec2<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureGather(t: texture_depth_cube_array,
                           s: sampler,
                           coords: vec3<f32>,
                           array_index: A) -> vec4<f32>

파라미터:

component 깊이 텍스처가 아닌 경우에만 적용됩니다.
선택된 텍셀에서 읽을 채널의 인덱스입니다.
제공되는 경우 component 표현식은 상수 표현식이어야 합니다(예: 1).
값은 최소 0, 최대 3이어야 합니다. 이 범위를 벗어나면 셰이더 생성 오류가 발생합니다.
t 샘플링된 또는 깊이 텍스처에서 읽어옵니다.
s 샘플러 타입입니다.
coords 텍스처 좌표입니다.
array_index 0부터 시작하는 텍스처 배열 인덱스.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

위에서 설명한 대로 선택된 텍셀의 지정된 채널에서 추출한 성분으로 구성된 네 성분 벡터를 반환합니다.

예시: 2D 텍스처에서 텍셀 성분 모으기
@group(0) @binding(0) var t: texture_2d<f32>;
@group(0) @binding(1) var dt: texture_depth_2d;
@group(0) @binding(2) var s: sampler;

fn gather_x_components(c: vec2<f32>) -> vec4<f32> {
  return textureGather(0,t,s,c);
}
fn gather_y_components(c: vec2<f32>) -> vec4<f32> {
  return textureGather(1,t,s,c);
}
fn gather_z_components(c: vec2<f32>) -> vec4<f32> {
  return textureGather(2,t,s,c);
}
fn gather_depth_components(c: vec2<f32>) -> vec4<f32> {
  return textureGather(dt,s,c);
}

17.7.3. textureGatherCompare

텍스처 갤러 비교 연산은 깊이 텍스처의 네 텍셀에 대해 깊이 비교를 수행하고, 결과를 하나의 벡터에 모읍니다. 방식은 다음과 같습니다:

파라미터화 오버로드
@must_use fn textureGatherCompare(t: texture_depth_2d,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  depth_ref: f32) -> vec4<f32>
@must_use fn textureGatherCompare(t: texture_depth_2d,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  depth_ref: f32,
                                  offset: vec2<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureGatherCompare(t: texture_depth_2d_array,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  array_index: A,
                                  depth_ref: f32) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureGatherCompare(t: texture_depth_2d_array,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  array_index: A,
                                  depth_ref: f32,
                                  offset: vec2<i32>) -> vec4<f32>
@must_use fn textureGatherCompare(t: texture_depth_cube,
                                  s: sampler_comparison,
                                  coords: vec3<f32>,
                                  depth_ref: f32) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureGatherCompare(t: texture_depth_cube_array,
                                  s: sampler_comparison,
                                  coords: vec3<f32>,
                                  array_index: A,
                                  depth_ref: f32) -> vec4<f32>

파라미터:

t 깊이 텍스처에서 읽어옵니다.
s 샘플러 비교입니다.
coords 텍스처 좌표입니다.
array_index 0부터 시작하는 텍스처 배열 인덱스입니다.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
depth_ref 샘플링된 깊이 값과 비교할 참조 값입니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

선택된 텍셀에 대해 비교 결과를 네 성분 벡터로 반환합니다. 위에서 설명한 방식대로 배치됩니다.

예시: 깊이 비교 모으기
@group(0) @binding(0) var dt: texture_depth_2d;
@group(0) @binding(1) var s: sampler;

fn gather_depth_compare(c: vec2<f32>, depth_ref: f32) -> vec4<f32> {
  return textureGatherCompare(dt,s,c,depth_ref);
}

17.7.4. textureLoad

샘플링이나 필터링 없이 텍스처에서 단일 텍셀을 읽어옵니다.

파라미터화 오버로드
Ci32 또는 u32
Li32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureLoad(t: texture_1d<ST>,
                         coords: C,
                         level: L) -> vec4<ST>
Ci32 또는 u32
Li32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureLoad(t: texture_2d<ST>,
                         coords: vec2<C>,
                         level: L) -> vec4<ST>
Ci32 또는 u32
Ai32 또는 u32
Li32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureLoad(t: texture_2d_array<ST>,
                        coords: vec2<C>,
                        array_index: A,
                        level: L) -> vec4<ST>
Ci32 또는 u32
Li32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureLoad(t: texture_3d<ST>,
                         coords: vec3<C>,
                         level: L) -> vec4<ST>
Ci32 또는 u32
Si32 또는 u32
STi32, u32, 또는 f32
@must_use fn textureLoad(t: texture_multisampled_2d<ST>,
                         coords: vec2<C>,
                         sample_index: S)-> vec4<ST>
Ci32 또는 u32
Li32 또는 u32
@must_use fn textureLoad(t: texture_depth_2d,
                         coords: vec2<C>,
                         level: L) -> f32
Ci32 또는 u32
Ai32 또는 u32
Li32 또는 u32
@must_use fn textureLoad(t: texture_depth_2d_array,
                         coords: vec2<C>,
                         array_index: A,
                         level: L) -> f32
Ci32 또는 u32
Si32 또는 u32
@must_use fn textureLoad(t: texture_depth_multisampled_2d,
                         coords: vec2<C>,
                         sample_index: S)-> f32
Ci32 또는 u32
@must_use fn textureLoad(t: texture_external,
                         coords: vec2<C>) -> vec4<f32>
Ci32 또는 u32
AMread 또는 read_write
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요.
@must_use fn textureLoad(t : texture_storage_1d<F, AM>,
                         coords : C) -> vec4<CF>
Ci32 또는 u32
AMread 또는 read_write
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요.
@must_use fn textureLoad(t : texture_storage_2d<F, AM>,
                         coords : vec2<C>) -> vec4<CF>
Ci32 또는 u32
AMread 또는 read_write
Ai32 또는 u32
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요.
@must_use fn textureLoad(t : texture_storage_2d_array<F, AM>,
                         coords : vec2<C>,
                         array_index : A) -> vec4<CF>
Ci32 또는 u32
AMread 또는 read_write
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요.
@must_use fn textureLoad(t : texture_storage_3d<F, AM>,
                         coords : vec3<C>) -> vec4<CF>

파라미터:

t 샘플링된, 멀티샘플링된, 깊이, 스토리지, 또는 외부 텍스처
coords 0부터 시작하는 텍셀 좌표
array_index 0부터 시작하는 텍스처 배열 인덱스
level mip 레벨이며, level 0은 텍스처의 전체 크기 버전을 포함합니다.
sample_index 멀티샘플링 텍스처의 0부터 시작하는 샘플 인덱스

반환:

필터링되지 않은 텍셀 데이터입니다.

논리 텍셀 주소가 다음 중 하나에 해당하면 유효하지 않습니다:

논리 텍셀 주소가 유효하지 않을 경우, 내장 함수는 다음 중 하나를 반환합니다:

17.7.5. textureNumLayers

배열형 텍스처의 레이어(요소) 수를 반환합니다.

파라미터화 오버로드
F텍셀 포맷
A액세스 모드
STi32, u32, 또는 f32

Ttexture_2d_array<ST>, texture_cube_array<ST>, texture_depth_2d_array, texture_depth_cube_array, 또는 texture_storage_2d_array<F,A>
@must_use fn textureNumLayers(t: T) -> u32

파라미터:

t 샘플링된, 깊이, 또는 스토리지 배열 텍스처

반환:

텍스처가 큐브 기반일 때는 큐브 배열 텍스처의 큐브 개수를 반환합니다.

그 외에는 배열형 텍스처의 레이어 수(텍셀의 동질 격자수)를 반환합니다.

17.7.6. textureNumLevels

텍스처의 mip 레벨 수를 반환합니다.

파라미터화 오버로드
STi32, u32, 또는 f32

Ttexture_1d<ST>, texture_2d<ST>, texture_2d_array<ST>, texture_3d<ST>, texture_cube<ST>, texture_cube_array<ST>, texture_depth_2d, texture_depth_2d_array, texture_depth_cube, 또는 texture_depth_cube_array
@must_use fn textureNumLevels(t: T) -> u32

파라미터:

t 샘플링된 또는 깊이 텍스처

반환:

텍스처의 mip 레벨 개수를 반환합니다.

17.7.7. textureNumSamples

멀티샘플링 텍스처에서 텍셀당 샘플 수를 반환합니다.

파라미터화 오버로드
STi32, u32, 또는 f32

Ttexture_multisampled_2d<ST> 또는 texture_depth_multisampled_2d
@must_use fn textureNumSamples(t: T) -> u32

파라미터:

t 멀티샘플링 텍스처

반환:

샘플 개수멀티샘플링 텍스처에 대해 반환합니다.

17.7.8. textureSample

텍스처를 샘플링합니다.

반드시 fragment 셰이더 단계에서만 사용해야 합니다.

균일성 분석이 이 함수 호출이 uniform control flow 내에 있음을 증명하지 못할 경우, derivative_uniformity 진단트리거됩니다.

파라미터화 오버로드
@must_use fn textureSample(t: texture_1d<f32>,
                           s: sampler,
                           coords: f32) -> vec4<f32>
@must_use fn textureSample(t: texture_2d<f32>,
                           s: sampler,
                           coords: vec2<f32>) -> vec4<f32>
@must_use fn textureSample(t: texture_2d<f32>,
                           s: sampler,
                           coords: vec2<f32>,
                           offset: vec2<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSample(t: texture_2d_array<f32>,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSample(t: texture_2d_array<f32>,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A,
                           offset: vec2<i32>) -> vec4<f32>
Ttexture_3d<f32> 또는 texture_cube<f32>
@must_use fn textureSample(t: T,
                           s: sampler,
                           coords: vec3<f32>) -> vec4<f32>
@must_use fn textureSample(t: texture_3d<f32>,
                           s: sampler,
                           coords: vec3<f32>,
                           offset: vec3<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSample(t: texture_cube_array<f32>,
                           s: sampler,
                           coords: vec3<f32>,
                           array_index: A) -> vec4<f32>
@must_use fn textureSample(t: texture_depth_2d,
                           s: sampler,
                           coords: vec2<f32>) -> f32
@must_use fn textureSample(t: texture_depth_2d,
                           s: sampler,
                           coords: vec2<f32>,
                           offset: vec2<i32>) -> f32
Ai32 또는 u32
@must_use fn textureSample(t: texture_depth_2d_array,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A) -> f32
Ai32 또는 u32
@must_use fn textureSample(t: texture_depth_2d_array,
                           s: sampler,
                           coords: vec2<f32>,
                           array_index: A,
                           offset: vec2<i32>) -> f32
@must_use fn textureSample(t: texture_depth_cube,
                           s: sampler,
                           coords: vec3<f32>) -> f32
Ai32 또는 u32
@must_use fn textureSample(t: texture_depth_cube_array,
                           s: sampler,
                           coords: vec3<f32>,
                           array_index: A) -> f32

파라미터:

t 샘플링된 또는 깊이 텍스처를 샘플링합니다.
s 샘플러 타입입니다.
coords 샘플링에 사용될 텍스처 좌표입니다.
array_index 샘플링할 0부터 시작하는 텍스처 배열 인덱스입니다.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

샘플링된 값입니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값이 반환됩니다.

17.7.9. textureSampleBias

텍스처 샘플링 시 mip 레벨에 바이어스를 적용합니다.

반드시 fragment 셰이더 단계에서만 사용해야 합니다.

균일성 분석이 이 함수 호출이 uniform control flow 내에 있음을 증명하지 못할 경우, derivative_uniformity 진단트리거됩니다.

파라미터화 오버로드
@must_use fn textureSampleBias(t: texture_2d<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               bias: f32) -> vec4<f32>
@must_use fn textureSampleBias(t: texture_2d<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               bias: f32,
                               offset: vec2<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleBias(t: texture_2d_array<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               array_index: A,
                               bias: f32) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleBias(t: texture_2d_array<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               array_index: A,
                               bias: f32,
                               offset: vec2<i32>) -> vec4<f32>
Ttexture_3d<f32> 또는 texture_cube<f32>
@must_use fn textureSampleBias(t: T,
                               s: sampler,
                               coords: vec3<f32>,
                               bias: f32) -> vec4<f32>
@must_use fn textureSampleBias(t: texture_3d<f32>,
                               s: sampler,
                               coords: vec3<f32>,
                               bias: f32,
                               offset: vec3<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleBias(t: texture_cube_array<f32>,
                               s: sampler,
                               coords: vec3<f32>,
                               array_index: A,
                               bias: f32) -> vec4<f32>

파라미터:

t 샘플링할 샘플링 텍스처입니다.
s 샘플러 타입입니다.
coords 샘플링에 사용될 텍스처 좌표입니다.
array_index 샘플링할 0부터 시작하는 텍스처 배열 인덱스입니다.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
bias 샘플링 전에 mip 레벨에 적용할 바이어스.
이 값은 [-16.0, 15.99] 범위로 클램핑됩니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

샘플링된 값입니다.

17.7.10. textureSampleCompare

깊이 텍스처를 샘플링하고, 샘플링된 깊이 값을 참조값과 비교합니다.

반드시 fragment 셰이더 단계에서만 사용해야 합니다.

균일성 분석이 이 함수 호출이 uniform control flow 내에 있음을 증명하지 못할 경우, derivative_uniformity 진단트리거됩니다.

파라미터화 오버로드
@must_use fn textureSampleCompare(t: texture_depth_2d,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  depth_ref: f32) -> f32
@must_use fn textureSampleCompare(t: texture_depth_2d,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  depth_ref: f32,
                                  offset: vec2<i32>) -> f32
Ai32 또는 u32
@must_use fn textureSampleCompare(t: texture_depth_2d_array,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  array_index: A,
                                  depth_ref: f32) -> f32
Ai32 또는 u32
@must_use fn textureSampleCompare(t: texture_depth_2d_array,
                                  s: sampler_comparison,
                                  coords: vec2<f32>,
                                  array_index: A,
                                  depth_ref: f32,
                                  offset: vec2<i32>) -> f32
@must_use fn textureSampleCompare(t: texture_depth_cube,
                                  s: sampler_comparison,
                                  coords: vec3<f32>,
                                  depth_ref: f32) -> f32
Ai32 또는 u32
@must_use fn textureSampleCompare(t: texture_depth_cube_array,
                                  s: sampler_comparison,
                                  coords: vec3<f32>,
                                  array_index: A,
                                  depth_ref: f32) -> f32

파라미터:

t 샘플링할 깊이 텍스처입니다.
s sampler_comparison 타입입니다.
coords 샘플링에 사용될 텍스처 좌표입니다.
array_index 샘플링할 0부터 시작하는 텍스처 배열 인덱스입니다.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
depth_ref 샘플링된 깊이 값과 비교할 참조 값입니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

[0.0..1.0] 범위의 값을 반환합니다.

각 샘플링된 텍셀은 sampler_comparison에 정의된 비교 연산자를 사용하여 참조 값과 비교되며, 각각 0 또는 1의 값을 갖게 됩니다.

샘플러가 bilinear 필터링을 사용하는 경우, 반환 값은 이 값들의 평균 필터 결과이고, 그렇지 않으면 단일 텍셀의 비교 결과가 반환됩니다.

uniform control flow가 아닌 곳에서 호출 시 불확정 값이 반환됩니다.

17.7.11. textureSampleCompareLevel

깊이 텍스처를 샘플링하고, 샘플링된 깊이 값을 참조값과 비교합니다.

파라미터화 오버로드
@must_use fn textureSampleCompareLevel(t: texture_depth_2d,
                                       s: sampler_comparison,
                                       coords: vec2<f32>,
                                       depth_ref: f32) -> f32
@must_use fn textureSampleCompareLevel(t: texture_depth_2d,
                                       s: sampler_comparison,
                                       coords: vec2<f32>,
                                       depth_ref: f32,
                                       offset: vec2<i32>) -> f32
Ai32 또는 u32
@must_use fn textureSampleCompareLevel(t: texture_depth_2d_array,
                                       s: sampler_comparison,
                                       coords: vec2<f32>,
                                       array_index: A,
                                       depth_ref: f32) -> f32
Ai32 또는 u32
@must_use fn textureSampleCompareLevel(t: texture_depth_2d_array,
                                       s: sampler_comparison,
                                       coords: vec2<f32>,
                                       array_index: A,
                                       depth_ref: f32,
                                       offset: vec2<i32>) -> f32
@must_use fn textureSampleCompareLevel(t: texture_depth_cube,
                                       s: sampler_comparison,
                                       coords: vec3<f32>,
                                       depth_ref: f32) -> f32
Ai32 또는 u32
@must_use fn textureSampleCompareLevel(t: texture_depth_cube_array,
                                       s: sampler_comparison,
                                       coords: vec3<f32>,
                                       array_index: A,
                                       depth_ref: f32) -> f32

파라미터:

t 샘플링할 깊이 텍스처입니다.
s sampler_comparison 타입입니다.
coords 샘플링에 사용될 텍스처 좌표입니다.
array_index 샘플링할 0부터 시작하는 텍스처 배열 인덱스입니다.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
depth_ref 샘플링된 깊이 값과 비교할 참조 값입니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

[0.0..1.0] 범위의 값을 반환합니다.

textureSampleCompareLevel 함수는 textureSampleCompare와 유사하지만, 다음 사항이 다릅니다:

17.7.12. textureSampleGrad

명시적으로 지정한 그래디언트를 사용하여 텍스처를 샘플링합니다.

파라미터화 오버로드
@must_use fn textureSampleGrad(t: texture_2d<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               ddx: vec2<f32>,
                               ddy: vec2<f32>) -> vec4<f32>
@must_use fn textureSampleGrad(t: texture_2d<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               ddx: vec2<f32>,
                               ddy: vec2<f32>,
                               offset: vec2<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleGrad(t: texture_2d_array<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               array_index: A,
                               ddx: vec2<f32>,
                               ddy: vec2<f32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleGrad(t: texture_2d_array<f32>,
                               s: sampler,
                               coords: vec2<f32>,
                               array_index: A,
                               ddx: vec2<f32>,
                               ddy: vec2<f32>,
                               offset: vec2<i32>) -> vec4<f32>
Ttexture_3d<f32> 또는 texture_cube<f32>
@must_use fn textureSampleGrad(t: T,
                               s: sampler,
                               coords: vec3<f32>,
                               ddx: vec3<f32>,
                               ddy: vec3<f32>) -> vec4<f32>
@must_use fn textureSampleGrad(t: texture_3d<f32>,
                               s: sampler,
                               coords: vec3<f32>,
                               ddx: vec3<f32>,
                               ddy: vec3<f32>,
                               offset: vec3<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleGrad(t: texture_cube_array<f32>,
                               s: sampler,
                               coords: vec3<f32>,
                               array_index: A,
                               ddx: vec3<f32>,
                               ddy: vec3<f32>) -> vec4<f32>

파라미터:

t 샘플링할 샘플링 텍스처입니다.
s 샘플러입니다.
coords 샘플링에 사용될 텍스처 좌표입니다.
array_index 샘플링할 0부터 시작하는 텍스처 배열 인덱스입니다.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
ddx 샘플 위치 계산에 사용되는 x 방향 도함수 벡터입니다.
ddy 샘플 위치 계산에 사용되는 y 방향 도함수 벡터입니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

샘플링된 값입니다.

17.7.13. textureSampleLevel

명시적으로 지정한 mip 레벨을 사용하여 텍스처를 샘플링합니다.

파라미터화 오버로드
@must_use fn textureSampleLevel(t: texture_1d<f32>,
                                s: sampler,
                                coords: f32,
                                level: f32) -> vec4<f32>
@must_use fn textureSampleLevel(t: texture_2d<f32>,
                                s: sampler,
                                coords: vec2<f32>,
                                level: f32) -> vec4<f32>
@must_use fn textureSampleLevel(t: texture_2d<f32>,
                                s: sampler,
                                coords: vec2<f32>,
                                level: f32,
                                offset: vec2<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleLevel(t: texture_2d_array<f32>,
                                s: sampler,
                                coords: vec2<f32>,
                                array_index: A,
                                level: f32) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleLevel(t: texture_2d_array<f32>,
                                s: sampler,
                                coords: vec2<f32>,
                                array_index: A,
                                level: f32,
                                offset: vec2<i32>) -> vec4<f32>
Ttexture_3d<f32> 또는 texture_cube<f32>
@must_use fn textureSampleLevel(t: T,
                                s: sampler,
                                coords: vec3<f32>,
                                level: f32) -> vec4<f32>
@must_use fn textureSampleLevel(t: texture_3d<f32>,
                                s: sampler,
                                coords: vec3<f32>,
                                level: f32,
                                offset: vec3<i32>) -> vec4<f32>
Ai32 또는 u32
@must_use fn textureSampleLevel(t: texture_cube_array<f32>,
                                s: sampler,
                                coords: vec3<f32>,
                                array_index: A,
                                level: f32) -> vec4<f32>
Li32 또는 u32
@must_use fn textureSampleLevel(t: texture_depth_2d,
                                s: sampler,
                                coords: vec2<f32>,
                                level: L) -> f32
Li32 또는 u32
@must_use fn textureSampleLevel(t: texture_depth_2d,
                                s: sampler,
                                coords: vec2<f32>,
                                level: L,
                                offset: vec2<i32>) -> f32
Ai32 또는 u32
Li32 또는 u32
@must_use fn textureSampleLevel(t: texture_depth_2d_array,
                                s: sampler,
                                coords: vec2<f32>,
                                array_index: A,
                                level: L) -> f32
Ai32 또는 u32
Li32 또는 u32
@must_use fn textureSampleLevel(t: texture_depth_2d_array,
                                s: sampler,
                                coords: vec2<f32>,
                                array_index: A,
                                level: L,
                                offset: vec2<i32>) -> f32
Li32 또는 u32
@must_use fn textureSampleLevel(t: texture_depth_cube,
                                s: sampler,
                                coords: vec3<f32>,
                                level: L) -> f32
Ai32 또는 u32
Li32 또는 u32
@must_use fn textureSampleLevel(t: texture_depth_cube_array,
                                s: sampler,
                                coords: vec3<f32>,
                                array_index: A,
                                level: L) -> f32

파라미터:

t 샘플링할 샘플링 또는 깊이 텍스처
s 샘플러 타입
coords 샘플링에 사용되는 텍스처 좌표
array_index 샘플링할 0부터 시작하는 텍스처 배열 인덱스.
이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다.
level mip 레벨입니다. level 0은 텍스처의 전체 크기 버전을 포함합니다. levelf32인 함수에서는, 값이 소수일 경우 Texture Format Capabilities에 따라 포맷이 필터링 가능하면 두 레벨 사이를 보간할 수 있습니다.
offset 샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다. 이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.
offset 표현식은 상수 표현식이어야 합니다(예: vec2<i32>(1, 2)).
offset 성분은 최소 -8, 최대 7이어야 합니다. 범위를 벗어나면 셰이더 생성 오류가 발생합니다.

반환:

샘플링된 값입니다.

17.7.14. textureSampleBaseClampToEdge

텍스처 뷰의 베이스 레벨에서 샘플링하며, 텍스처 좌표는 아래와 같이 엣지에 클램핑됩니다.

파라미터화 오버로드
Ttexture_2d<f32> 또는 texture_external
@must_use fn textureSampleBaseClampToEdge(t: T,
                                          s: sampler,
                                          coords: vec2<f32>) -> vec4<f32>

파라미터:

t 샘플링할 샘플링 또는 외부 텍스처
s 샘플러 타입
coords 샘플링에 사용되는 텍스처 좌표

샘플링 전에 지정한 좌표는 다음 사각형으로 클램핑됩니다.

[ half_texel, 1 - half_texel ]

여기서

half_texel = vec2(0.5) / vec2<f32>(textureDimensions(t))

Note: half-texel 보정은 샘플러의 addressingfilter 모드와 관계없이 래핑이 발생하지 않도록 보장합니다. 즉, 엣지 근처에서 샘플링할 때 샘플된 텍셀은 해당 엣지에 위치하거나 인접하며, 반대쪽 엣지에서 선택되지 않습니다.

반환:

샘플링된 값입니다.

17.7.15. textureStore

단일 텍셀을 텍스처에 기록합니다.

파라미터화 오버로드
F텍셀 포맷
Ci32 또는 u32
AMwrite 또는 read_write
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요.
fn textureStore(t: texture_storage_1d<F,AM>,
                coords: C,
                value: vec4<CF>)
F텍셀 포맷
Ci32 또는 u32
AMwrite 또는 read_write
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요.
fn textureStore(t: texture_storage_2d<F,AM>,
                coords: vec2<C>,
                value: vec4<CF>)
F텍셀 포맷
Ci32 또는 u32
AMwrite 또는 read_write
Ai32 또는 u32
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요.
fn textureStore(t: texture_storage_2d_array<F,AM>,
                coords: vec2<C>,
                array_index: A,
                value: vec4<CF>)
F텍셀 포맷
Ci32 또는 u32
AMwrite 또는 read_write
CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요.
fn textureStore(t: texture_storage_3d<F,AM>,
                coords: vec3<C>,
                value: vec4<CF>)

파라미터:

t write-only storage 텍스처 또는 read-write storage 텍스처
coords 0부터 시작하는 텍셀 좌표
array_index 0부터 시작하는 텍스처 배열 인덱스
value 새 텍셀 값입니다. value역 채널 전송 함수를 사용해 변환됩니다.

Note:

논리 텍셀 주소가 다음 중 하나에 해당하면 유효하지 않습니다:

논리 텍셀 주소가 유효하지 않은 경우, 내장 함수는 실행되지 않습니다.

17.8. 원자 내장 함수

원자 내장 함수는 원자 객체를 읽기/쓰기/읽기-수정-쓰기 하는 데 사용할 수 있습니다. § 6.2.8 원자 타입에서 허용된 유일한 연산입니다.

모든 원자 내장 함수는 relaxed 메모리 순서를 사용합니다. 이는 동기화와 순서 보장이 동일한 메모리 위치에서의 원자 연산들 사이에만 적용된다는 의미입니다. 원자와 비원자 접근, 또는 서로 다른 메모리 위치에 대한 원자 접근 사이에는 동기화나 순서 보장이 없습니다.

원자 내장 함수는 절대 정점 셰이더 단계에서 사용되어서는 안 됩니다.

모든 원자 내장 함수에서 atomic_ptr 파라미터의 주소 공간 AS반드시 storage 또는 workgroup이어야 합니다.

T반드시 u32 또는 i32이어야 합니다.

17.8.1. atomicLoad

fn atomicLoad(atomic_ptr: ptr<AS, atomic<T>, read_write>) -> T

atomic_ptr가 가리키는 값을 원자적으로 읽어 반환합니다. 객체를 수정하지 않습니다.

17.8.2. atomicStore

fn atomicStore(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T)

atomic_ptr가 가리키는 원자 객체에 값 v를 원자적으로 저장합니다.

17.8.3. 원자 읽기-수정-쓰기 산술 및 논리 함수

각 함수는 다음 단계를 원자적으로 수행합니다:

  1. atomic_ptr가 가리키는 원래 값을 읽습니다.

  2. 함수 이름에 따른 연산(예: max)을 값 v로 수행하여 새 값을 얻습니다.

  3. 새 값을 atomic_ptr에 저장합니다.

각 함수는 연산 이전에 원자 객체에 저장된 원래 값을 반환합니다.

17.8.3.1. atomicAdd
fn atomicAdd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 v 값을 더하는 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.

예시: 함수로서의 원자 덧셈 연산
// 모든 연산은 원자적으로 수행됨
fn atomicAdd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = old + v;
  return old;
}
17.8.3.2. atomicSub
fn atomicSub(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 v 값을 빼는 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.

예시: 함수로서의 원자 뺄셈 연산
// 모든 연산은 원자적으로 수행됨
fn atomicSub(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = old - v;
  return old;
}
17.8.3.3. atomicMax
fn atomicMax(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 v 값과의 최대값 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.

예시: 함수로서의 원자 최대값 연산
// 모든 연산은 원자적으로 수행됨
fn atomicMax(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = max(old, v);
  return old;
}
17.8.3.4. atomicMin
fn atomicMin(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 v 값과의 최소값 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.

예시: 함수로서의 원자 최소값 연산
// 모든 연산은 원자적으로 수행됨
fn atomicMin(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = min(old, v);
  return old;
}
17.8.3.5. atomicAnd
fn atomicAnd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 v 값과의 비트 AND 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.

예시: 함수로서의 원자 비트 AND 연산
// 모든 연산은 원자적으로 수행됨
fn atomicAnd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = old & v;
  return old;
}
17.8.3.6. atomicOr
fn atomicOr(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 v 값과의 비트 OR 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.

예시: 함수로서의 원자 비트 OR 연산
// 모든 연산은 원자적으로 수행됨
fn atomicOr(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = old | v;
  return old;
}
17.8.3.7. atomicXor
fn atomicXor(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 대해 값 v와 비트 단위 XOR 연산을 원자적으로 수행하고, 연산 이전에 원자 객체에 저장되어 있던 원래 값을 반환합니다.

예시: 함수로서 원자적 비트 XOR 연산 동작
// 모든 연산은 원자적으로 수행됩니다
fn atomicXor(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = old ^ v;
  return old;
}

17.8.4. atomicExchange

fn atomicExchange(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T

atomic_ptr가 가리키는 원자 객체에 값 v를 원자적으로 저장하고, 연산 이전에 원자 객체에 저장되어 있던 원래 값을 반환합니다.

예시: 함수로서 원자적 교환 연산 동작
// 모든 연산은 원자적으로 수행됩니다
fn atomicExchange(atomic_ptr: ptr<AS, atomic<T>, read_write>, v : T) -> T {
  let old = *atomic_ptr;
  *atomic_ptr = v;
  return old;
}

17.8.5. atomicCompareExchangeWeak

fn atomicCompareExchangeWeak(
      atomic_ptr: ptr<AS, atomic<T>, read_write>,
      cmp: T,
      v: T) -> __atomic_compare_exchange_result<T>

struct __atomic_compare_exchange_result<T> {
  old_value : T,   // old value stored in the atomic
  exchanged : bool // true if the exchange was done
}

참고: __atomic_compare_exchange_result 타입으로 값을 명시적으로 선언할 수는 없지만, 값의 타입이 추론될 수 있습니다.

다음 단계들이 원자적으로 수행됩니다:

  1. atomic_ptr가 가리키는 원래 값을 로드합니다.

  2. 원래 값과 cmp 값을 동등 비교 연산으로 비교합니다.

  3. 동등 비교 연산 결과가 true인 경우에만 v 값을 저장합니다.

두 개의 멤버를 가진 구조체를 반환하며, 첫 번째 멤버인 old_value는 연산 이전 원자 객체의 원래 값이고 두 번째 멤버 exchanged는 비교가 성공했는지 여부입니다.

예시: 함수로서 원자적 비교 교환 연산 동작
// 모든 연산은 원자적으로 수행됩니다
fn atomicCompareExchangeWeak(atomic_ptr: ptr<AS, atomic<T>, read_write>, cmp : T, v : T) ->
  _atomic_compare_exchange_result<T> {
  let old = *atomic_ptr;
  // 이 비교는 허위 실패가 발생할 수 있습니다.
  let comparison = old == cmp;
  if comparison {
    *atomic_ptr = v;
  }
  return _atomic_compare_exchange_result<T>(old, comparison);
}

참고: 일부 구현에서 동등 비교 연산이 허위 실패할 수 있습니다. 즉, 결과 벡터의 두 번째 구성 요소가 false가 될 수 있으며, 첫 번째 구성 요소는 cmp와 같을 수 있습니다.

17.9. 데이터 패킹 내장 함수

데이터 패킹 내장 함수는 WGSL 타입과 직접적으로 일치하지 않는 데이터 형식을 사용하여 값을 인코딩할 수 있습니다. 이를 통해 프로그램은 많은 값을 메모리에 밀집하게 기록할 수 있으며, 셰이더의 메모리 대역폭 요구를 줄일 수 있습니다.

각 내장 함수는 여러 입력 값에 채널 전달 함수를 적용한 후, 결과를 하나의 출력 값으로 결합합니다.

참고: unorm 값을 패킹할 때, 정규화된 부동소수점 값의 범위는 [0.0, 1.0]입니다.

참고: snorm 값을 패킹할 때, 정규화된 부동소수점 값의 범위는 [-1.0, 1.0]입니다.

17.9.1. pack4x8snorm

오버로드
@const @must_use fn pack4x8snorm(e: vec4<f32>) -> u32
설명 네 개의 정규화된 부동소수점 값을 8비트 부호 있는 정수로 변환한 후, 이를 하나의 u32 값으로 결합합니다.

입력의 e[i] 컴포넌트는 8비트 2의 보수 정수 값 ⌊ 0.5 + 127 × min(1, max(-1, e[i])) ⌋로 변환되며, 이 값은 결과의 8 × i 부터 8 × i + 7 비트에 배치됩니다.

17.9.2. pack4x8unorm

오버로드
@const @must_use fn pack4x8unorm(e: vec4<f32>) -> u32
설명 네 개의 정규화된 부동소수점 값을 8비트 부호 없는 정수로 변환한 후, 이를 하나의 u32 값으로 결합합니다.

입력의 e[i] 컴포넌트는 8비트 부호 없는 정수 값 ⌊ 0.5 + 255 × min(1, max(0, e[i])) ⌋로 변환되며, 이 값은 결과의 8 × i 부터 8 × i + 7 비트에 배치됩니다.

17.9.3. pack4xI8

오버로드
@const @must_use fn pack4xI8(e: vec4<i32>) -> u32
설명 e의 각 컴포넌트의 하위 8비트만 u32 값에 패킹하고, 사용되지 않는 비트는 모두 버립니다.

입력의 e[i] 컴포넌트는 결과의 8 × i 부터 8 × i + 7 비트에 매핑됩니다.

17.9.4. pack4xU8

오버로드
@const @must_use fn pack4xU8(e: vec4<u32>) -> u32
설명 e의 각 컴포넌트의 하위 8비트만 u32 값에 패킹하고, 사용되지 않는 비트는 모두 버립니다.

입력의 e[i] 컴포넌트는 결과의 8 × i 부터 8 × i + 7 비트에 매핑됩니다.

17.9.5. pack4xI8Clamp

오버로드
@const @must_use fn pack4xI8Clamp(e: vec4<i32>) -> u32
설명 e의 각 컴포넌트를 [-128, 127] 범위로 클램프한 후, 각 컴포넌트의 하위 8비트를 u32 값에 패킹합니다.

입력의 e[i] 컴포넌트는 결과의 8 × i 부터 8 × i + 7 비트에 매핑됩니다.

17.9.6. pack4xU8Clamp

오버로드
@const @must_use fn pack4xU8Clamp(e: vec4<u32>) -> u32
설명 e의 각 컴포넌트를 [0, 255] 범위로 클램프한 후, 각 컴포넌트의 하위 8비트를 u32 값에 패킹합니다.

입력의 e[i] 컴포넌트는 결과의 8 × i 부터 8 × i + 7 비트에 매핑됩니다.

17.9.7. pack2x16snorm

오버로드
@const @must_use fn pack2x16snorm(e: vec2<f32>) -> u32
설명 두 개의 정규화된 부동소수점 값을 16비트 부호 있는 정수로 변환한 후, 이를 하나의 u32 값으로 결합합니다.
입력의 e[i] 컴포넌트는 16비트 2의 보수 정수 값 ⌊ 0.5 + 32767 × min(1, max(-1, e[i])) ⌋로 변환되며, 이 값은 결과의 16 × i 부터 16 × i + 15 비트에 배치됩니다.

17.9.8. pack2x16unorm

오버로드
@const @must_use fn pack2x16unorm(e: vec2<f32>) -> u32
설명 두 개의 정규화된 부동소수점 값을 16비트 부호 없는 정수로 변환한 후, 이를 하나의 u32 값으로 결합합니다.
입력의 e[i] 컴포넌트는 16비트 부호 없는 정수 값 ⌊ 0.5 + 65535 × min(1, max(0, e[i])) ⌋로 변환되며, 이 값은 결과의 16 × i 부터 16 × i + 15 비트에 배치됩니다.

17.9.9. pack2x16float

오버로드
@const @must_use fn pack2x16float(e: vec2<f32>) -> u32
설명 두 개의 부동소수점 값을 half-precision 부동소수점 값으로 변환한 후, 이를 하나의 u32 값으로 결합합니다.
입력의 e[i] 컴포넌트는 IEEE-754 binary16 값으로 변환되며, 이 값은 결과의 16 × i 부터 16 × i + 15 비트에 배치됩니다. § 15.7.6 부동소수점 변환을 참고하세요.

만약 e[0] 또는 e[1]이 binary16의 유한 범위를 벗어나면 다음과 같습니다:

17.10. 데이터 언패킹 내장 함수

데이터 언패킹 내장 함수는 WGSL 타입과 직접적으로 일치하지 않는 데이터 형식의 값을 디코딩하는 데 사용할 수 있습니다. 이를 통해 프로그램은 메모리에서 많은 밀집된 값을 읽을 수 있으며, 셰이더의 메모리 대역폭 요구를 줄일 수 있습니다.

각 내장 함수는 입력 값을 채널로 분리한 다음, 각 채널에 채널 전달 함수를 적용합니다.

참고: unorm 값을 언패킹할 때, 정규화된 부동소수점 결과는 [0.0, 1.0] 구간에 있습니다.

참고: snorm 값을 언패킹할 때, 정규화된 부동소수점 결과는 [-1.0, 1.0] 구간에 있습니다.

17.10.1. unpack4x8snorm

오버로드
@const @must_use fn unpack4x8snorm(e: u32) -> vec4<f32>
설명 32비트 값을 네 개의 8비트 조각으로 분해한 뒤, 각 조각을 부호 있는 정규화된 부동소수점 값으로 재해석합니다.
결과의 i 컴포넌트는 max(v ÷ 127, -1)이며, ve의 8×i부터 8×i + 7 비트까지를 2의 보수 부호 있는 정수로 해석한 값입니다.

17.10.2. unpack4x8unorm

오버로드
@const @must_use fn unpack4x8unorm(e: u32) -> vec4<f32>
설명 32비트 값을 네 개의 8비트 조각으로 분해한 뒤, 각 조각을 부호 없는 정규화된 부동소수점 값으로 재해석합니다.
결과의 i 컴포넌트는 v ÷ 255이며, ve의 8×i부터 8×i + 7 비트까지를 부호 없는 정수로 해석한 값입니다.

17.10.3. unpack4xI8

오버로드
@const @must_use fn unpack4xI8(e: u32) -> vec4<i32>
설명 e는 네 개의 8비트 부호 있는 정수 컴포넌트로 이루어진 벡터로 해석됩니다. e를 부호 확장하여 vec4<i32>로 언패킹합니다.

17.10.4. unpack4xU8

오버로드
@const @must_use fn unpack4xU8(e: u32) -> vec4<u32>
설명 e는 네 개의 8비트 부호 없는 정수 컴포넌트로 이루어진 벡터로 해석됩니다. e를 0 확장하여 vec4<u32>로 언패킹합니다.

17.10.5. unpack2x16snorm

오버로드
@const @must_use fn unpack2x16snorm(e: u32) -> vec2<f32>
설명 32비트 값을 두 개의 16비트 조각으로 분해한 뒤, 각 조각을 부호 있는 정규화된 부동소수점 값으로 재해석합니다.
결과의 i 컴포넌트는 max(v ÷ 32767, -1)이며, ve의 16×i부터 16×i + 15 비트까지를 2의 보수 부호 있는 정수로 해석한 값입니다.

17.10.6. unpack2x16unorm

오버로드
@const @must_use fn unpack2x16unorm(e: u32) -> vec2<f32>
설명 32비트 값을 두 개의 16비트 조각으로 분해한 뒤, 각 조각을 부호 없는 정규화된 부동소수점 값으로 재해석합니다.
결과의 i 컴포넌트는 v ÷ 65535이며, ve의 16×i부터 16×i + 15 비트까지를 부호 없는 정수로 해석한 값입니다.

17.10.7. unpack2x16float

오버로드
@const @must_use fn unpack2x16float(e: u32) -> vec2<f32>
설명 32비트 값을 두 개의 16비트 조각으로 분해한 뒤, 각 조각을 부동소수점 값으로 재해석합니다.
결과의 i 컴포넌트는 v의 f32 표현이며, ve의 16×i부터 16×i + 15 비트까지를 IEEE-754 binary16 값으로 해석한 것입니다. § 15.7.6 부동소수점 변환을 참고하세요.

17.11. 동기화 내장 함수

모든 동기화 함수는 Acquire/Release 메모리 순서를 가진 제어 배리어를 실행합니다. 즉, 모든 동기화 함수와 영향받는 메모리 및 원자 연산들은 동기화 함수와 관련하여 프로그램 순서로 정렬됩니다. 또한, 동기화 함수 이전에 프로그램 순서로 배치된 영향받는 메모리와 원자 연산은 해당 워크그룹 내의 모든 다른 스레드에게 보여진 후, 동기화 함수 이후에 프로그램 순서로 배치된 영향받는 메모리 또는 원자 연산이 워크그룹 멤버에 의해 실행될 수 있습니다.

모든 동기화 함수는 Workgroup 메모리 범위를 사용합니다.
모든 동기화 함수는 Workgroup 실행 범위를 가집니다.
모든 동기화 함수는 컴퓨트 셰이더 스테이지에서만 사용되어야 합니다. 모든 동기화 함수는 균일 제어 흐름 내에서만 호출되어야 합니다.

17.11.1. storageBarrier

오버로드
fn storageBarrier()
설명 storage 주소 공간의 메모리 및 원자 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다.

17.11.2. textureBarrier

오버로드
fn textureBarrier()
설명 handle 주소 공간의 메모리 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다.

17.11.3. workgroupBarrier

오버로드
fn workgroupBarrier()
설명 workgroup 주소 공간의 메모리 및 원자 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다.

17.11.4. workgroupUniformLoad

오버로드
@must_use fn workgroupUniformLoad(p : ptr<workgroup, T>) -> T
파라미터화 T구체적 구성 가능 타입입니다.
설명 워크그룹 내 모든 호출에 p가 가리키는 값을 반환합니다. 반환값은 균일 값입니다. p균일 값이어야 합니다.

workgroup 주소 공간의 메모리 및 원자 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다.

오버로드
@must_use fn workgroupUniformLoad(p : ptr<workgroup, atomic<T>, read_write>) -> T
설명 p가 가리키는 값을 원자적으로 로드하여 워크그룹 내 모든 호출에 반환합니다. 반환값은 균일 값입니다. p균일 값이어야 합니다.

workgroup 주소 공간의 메모리 및 원자 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다.

17.12. 서브그룹 내장 함수

§ 15.6.3 서브그룹 연산을 참고하세요.

이 함수들에 대한 호출은 다음과 같습니다:

참고: 컴퓨트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 워크그룹입니다. 프래그먼트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 드로우 명령입니다. 이 두 범위 모두 서브그룹보다 큽니다.

17.12.1. subgroupAdd

오버로드
@must_use fn subgroupAdd(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다.
설명 축소 연산.

서브그룹 내 모든 활성 호출들 중 e의 합을 반환합니다.

17.12.1.1. subgroupExclusiveAdd
오버로드
@must_use fn subgroupExclusiveAdd(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다.
설명 배타적 프리픽스 스캔 연산.

현재 호출의 서브그룹 호출 ID보다 작은 서브그룹 내 모든 활성 호출들 중 e의 합을 반환합니다.

활성 호출들 중 가장 낮은 id를 가진 호출의 반환값은 T(0)입니다.

17.12.1.2. subgroupInclusiveAdd
오버로드
@must_use fn subgroupInclusiveAdd(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다.
설명 포괄적 프리픽스 스캔 연산.

현재 호출의 서브그룹 호출 ID보다 작거나 같은 서브그룹 내 모든 활성 호출들 중 e의 합을 반환합니다.

참고: subgroupExclusiveAdd(x) + x와 동일합니다.

17.12.2. subgroupAll

오버로드
@must_use fn subgroupAll(e : bool) -> bool
설명 활성subgroup 내 모든 호출에서 etrue일 때 true를 반환합니다.

17.12.3. subgroupAnd

오버로드
@must_use fn subgroupAnd(e : T) -> T
사전조건 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다
설명 축소 연산.

활성subgroup 내 모든 호출의 e에 대해 비트 단위 and(&) 결과를 반환합니다.

17.12.4. subgroupAny

오버로드
@must_use fn subgroupAny(e : bool) -> bool
설명 활성subgroup 내 어떤 호출이라도 etrue이면 true를 반환합니다.

17.12.5. subgroupBallot

오버로드
@must_use fn subgroupBallot(pred : bool) -> vec4<u32>
설명 활성subgroup 내에서 predtrue인 호출들에 대한 비트마스크를 반환합니다.

반환값의 x 컴포넌트는 호출 0부터 31까지를 포함합니다.
y 컴포넌트는 32부터 63까지,
z 컴포넌트는 64부터 95까지,
w 컴포넌트는 96부터 127까지 포함합니다.

각 컴포넌트 내에서는 ID가 비트 위치에 따라 오름차순입니다 (예: ID 32는 y 컴포넌트의 비트 위치 0에 있습니다).

17.12.6. subgroupBroadcast

오버로드
@must_use fn subgroupBroadcast(e : T, id : I) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터
Iu32 또는 i32입니다
설명 subgroup 호출 IDid와 일치하는 호출의 e 값을 모든 활성subgroup 호출에 반환합니다.

id는 [0, 128) 범위의 const-expression이어야 합니다.

id활성된 호출을 선택하지 않으면 동적 오류입니다.

참고: 상수가 아닌 버전의 id가 필요하면 subgroupShuffle을 대신 사용하세요.

17.12.6.1. subgroupBroadcastFirst
오버로드
@must_use fn subgroupBroadcastFirst(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 subgroup 호출 ID활성 호출 중 가장 낮은 호출의 e 값을 subgroup 내 모든 활성 호출에 반환합니다.

17.12.7. subgroupElect

오버로드
@must_use fn subgroupElect() -> bool
설명 현재 호출이 subgroup 호출 ID활성 호출 중 가장 낮을 경우 true를 반환합니다.

17.12.8. subgroupMax

오버로드
@must_use fn subgroupMax(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 축소 연산.

활성subgroup 내 모든 호출의 e 중 최대값을 반환합니다.

17.12.9. subgroupMin

오버로드
@must_use fn subgroupMin(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 축소 연산.

활성subgroup 내 모든 호출의 e 중 최소값을 반환합니다.

17.12.10. subgroupMul

오버로드
@must_use fn subgroupMul(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 축소 연산.

활성subgroup 내 모든 호출의 e의 곱을 반환합니다.

17.12.10.1. subgroupExclusiveMul
오버로드
@must_use fn subgroupExclusiveMul(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 배타적 프리픽스 스캔 연산.

현재 호출의 subgroup 호출 ID보다 작은 subgroup 내 모든 활성 호출의 e의 곱을 반환합니다.

활성 호출 중 가장 낮은 id의 호출에 대해 반환값은 T(1)입니다.

17.12.10.2. subgroupInclusiveMul
오버로드
@must_use fn subgroupInclusiveMul(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 포괄적 프리픽스 스캔 연산.

현재 호출의 subgroup 호출 ID보다 작거나 같은 subgroup 내 모든 활성 호출의 e의 곱을 반환합니다.

참고: subgroupExclusiveMul(x) * x와 동일합니다.

17.12.11. subgroupOr

오버로드
@must_use fn subgroupOr(e : T) -> T
사전조건 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다
설명 축소 연산.

활성subgroup 내 모든 호출의 e에 대해 비트 단위 or(|) 결과를 반환합니다.

17.12.12. subgroupShuffle

오버로드
@must_use fn subgroupShuffle(e : T, id : I) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
Iu32 또는 i32입니다
설명 subgroup 호출 IDid와 일치하는 호출의 e 값을 반환합니다.

만약 id가 [0, 128) 범위를 벗어나면:

id활성 호출을 선택하지 않으면 불확정 값이 반환됩니다.

17.12.12.1. subgroupShuffleDown
오버로드
@must_use fn subgroupShuffleDown(e : T, delta : u32) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 현재 호출에 대해 subgroup 호출 IDsubgroup_invocation_id + delta와 일치하는 호출의 e 값을 반환합니다.

delta가 127보다 크면:

delta균일 값이 아니면 subgroup_uniformity 경고트리거됩니다. subgroup_invocation_id + delta활성 호출을 선택하지 않거나 delta가 subgroup 내에서 균일 값이 아니면 불확정 값이 반환됩니다.

17.12.12.2. subgroupShuffleUp
오버로드
@must_use fn subgroupShuffleUp(e : T, delta : u32) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 현재 호출에 대해 subgroup 호출 IDsubgroup_invocation_id - delta와 일치하는 호출의 e 값을 반환합니다.

delta가 127보다 크면:

delta균일 값이 아니면 subgroup_uniformity 경고트리거됩니다. subgroup_invocation_id - delta활성 호출을 선택하지 않거나 delta가 subgroup 내에서 균일 값이 아니면 불확정 값이 반환됩니다.

17.12.12.3. subgroupShuffleXor
오버로드
@must_use fn subgroupShuffleXor(e : T,  mask : u32) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 현재 호출에 대해 subgroup 호출 IDsubgroup_invocation_id ^ mask와 일치하는 호출의 e 값을 반환합니다.

mask가 127보다 크면:

mask균일 값이 아니면 subgroup_uniformity 경고트리거됩니다. mask활성 호출을 선택하지 않거나 mask가 subgroup 내에서 균일 값이 아니면 불확정 값이 반환됩니다.

17.12.13. subgroupXor

오버로드
@must_use fn subgroupXor(e : T) -> T
사전조건 T는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다
설명 축소 연산.

활성subgroup 내 모든 호출의 e에 대해 비트 단위 xor(^) 결과를 반환합니다.

17.13. 쿼드 연산

§ 15.6.4 쿼드 연산 참고.

이 함수들에 대한 호출은 다음과 같습니다:

참고: 컴퓨트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 워크그룹입니다. 프래그먼트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 드로우 명령입니다. 이 두 범위 모두 쿼드보다 큽니다.

17.13.1. quadBroadcast

오버로드
@must_use fn quadBroadcast(e : T, id : I) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
Iu32 또는 i32입니다
설명 쿼드 내에서 quad 호출 IDid와 일치하는 호출의 e 값을 모든 활성된 쿼드 호출에 반환합니다.

id는 [0, 4) 범위의 const-expression이어야 합니다.

id활성 호출을 선택하지 않으면 불확정 값이 반환됩니다.

참고: subgroupBroadcast와 달리, 현재 상수가 아닌 대안은 없습니다.

17.13.2. quadSwapDiagonal

오버로드
@must_use fn quadSwapDiagonal(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 쿼드 내에서 반대 좌표를 가진 호출의 e 값을 반환합니다. 즉,
  • ID 0과 3이 교환됩니다.

  • ID 1과 2가 교환됩니다.

17.13.3. quadSwapX

오버로드
@must_use fn quadSwapX(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 쿼드 내에서 동일한 X 차원을 공유하는 호출의 e 값을 반환합니다. 즉,
  • ID 0과 1이 교환됩니다.

  • ID 2와 3이 교환됩니다.

17.13.4. quadSwapY

오버로드
@must_use fn quadSwapY(e : T) -> T
사전조건 T구체적 숫자 스칼라 또는 숫자 벡터입니다
설명 쿼드 내에서 동일한 Y 차원을 공유하는 호출의 e 값을 반환합니다. 즉,
  • ID 0과 2가 교환됩니다.

  • ID 1과 3이 교환됩니다.

18. 재귀적 하강 파싱을 위한 문법

이 절은 규범적이지 않습니다.

WGSL 문법은 LALR(1) 파서에 적합한 형태로 명세되어 있습니다. 구현에서는 재귀적 하강 파서를 사용하고 싶을 수도 있습니다.

규범적인 문법은 재귀적 하강 파서에서 직접 사용할 수 없습니다. 여러 규칙이 좌측 재귀적이기 때문입니다. 문법 규칙이 직접 좌측 재귀적인 경우는 정의되는 비단말 기호가 그 생성식의 첫 번째로 등장할 때입니다.

아래는 WGSL 문법을 기계적으로 변환한 것입니다:

하지만 LL(1)은 아닙니다. 일부 비단말 기호의 여러 생성식은 공통 전방탐색 집합을 가집니다. 예를 들어, attribute 비단말의 모든 생성식은 attr 토큰으로 시작합니다. 더 미묘한 예로는 global_decl이 있는데, 세 개의 생성식이 attribute *로 시작하지만 이후 fn, override, var 토큰에 의해 구분됩니다.

간결함을 위해 많은 토큰 정의는 반복하지 않습니다. 토큰 정의는 명세의 본문에서 참고하세요.

additive_operator:

| '+'

| '-'

argument_expression_list:

| '(' ( expression ( ',' expression )* ',' ? )? ')'

assignment_statement/0.1:

| compound_assignment_operator

| '='

attribute:

| compute_attr

| const_attr

| fragment_attr

| interpolate_attr

| invariant_attr

| must_use_attr

| vertex_attr

| workgroup_size_attr

| '@' ident_pattern_token ( '(' ( expression ( ',' expression )* ',' ? )? ')' )?

| '@' 'align' '(' expression ',' ? ')'

| '@' 'binding' '(' expression ',' ? ')'

| '@' 'blend_src' '(' expression ',' ? ')'

| '@' 'builtin' '(' builtin_value_name ',' ? ')'

| '@' 'diagnostic' diagnostic_control

| '@' 'group' '(' expression ',' ? ')'

| '@' 'id' '(' expression ',' ? ')'

| '@' 'location' '(' expression ',' ? ')'

| '@' 'size' '(' expression ',' ? ')'

bitwise_expression.post.unary_expression:

| '&' unary_expression ( '&' unary_expression )*

| '^' unary_expression ( '^' unary_expression )*

| '|' unary_expression ( '|' unary_expression )*

bool_literal:

| 'false'

| 'true'

builtin_value_name: ident_pattern_token
case_selector:

| expression

| 'default'

component_or_swizzle_specifier:

| '.' member_ident component_or_swizzle_specifier ?

| '.' swizzle_name component_or_swizzle_specifier ?

| '[' expression ']' component_or_swizzle_specifier ?

compound_assignment_operator:

| shift_left_assign

| shift_right_assign

| '%='

| '&='

| '*='

| '+='

| '-='

| '/='

| '^='

| '|='

compound_statement:

| attribute * '{' statement * '}'

compute_attr:

| '@' 'compute'

const_attr:

| '@' 'const'

core_lhs_expression:

| ident

| '(' lhs_expression ')'

decimal_float_literal:

| /0[fh]/

| /[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?[fh]?/

| /[0-9]+[eE][+-]?[0-9]+[fh]?/

| /[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?[fh]?/

| /[1-9][0-9]*[fh]/

decimal_int_literal:

| /0[iu]?/

| /[1-9][0-9]*[iu]?/

diagnostic_control:

| '(' ident_pattern_token ',' diagnostic_rule_name ',' ? ')'

diagnostic_rule_name:

| ident_pattern_token

| ident_pattern_token '.' ident_pattern_token

expression:

| unary_expression bitwise_expression.post.unary_expression

| unary_expression relational_expression.post.unary_expression

| unary_expression relational_expression.post.unary_expression '&&' unary_expression relational_expression.post.unary_expression ( '&&' unary_expression relational_expression.post.unary_expression )*

| unary_expression relational_expression.post.unary_expression '||' unary_expression relational_expression.post.unary_expression ( '||' unary_expression relational_expression.post.unary_expression )*

float_literal:

| decimal_float_literal

| hex_float_literal

for_init:

| ident func_call_statement.post.ident

| variable_or_value_statement

| variable_updating_statement

for_update:

| ident func_call_statement.post.ident

| variable_updating_statement

fragment_attr:

| '@' 'fragment'

func_call_statement.post.ident:

| template_elaborated_ident.post.ident '(' ( expression ( ',' expression )* ',' ? )? ')'

global_assert:

| 'const_assert' ';'

global_decl:

| attribute * 'fn' ident '(' ( attribute * ident ':' type_specifier ( ',' param )* ',' ? )? ')' ( '->' attribute * ident template_elaborated_ident.post.ident )? attribute * '{' statement * '}'

| attribute * 'var' ( _template_args_start expression ( ',' expression )* ',' ? _template_args_end )? optionally_typed_ident ( '=' expression )? ';'

| global_value_decl ';'

| 'alias' ident '=' ident template_elaborated_ident.post.ident ';'

| 'struct' ident '{' attribute * member_ident ':' type_specifier ( ',' attribute * member_ident ':' type_specifier )* ',' ? '}'

global_directive:

| 'diagnostic' '(' ident_pattern_token ',' diagnostic_rule_name ',' ? ')' ';'

| 'enable' ident_pattern_token ( ',' ident_pattern_token )* ',' ? ';'

| 'requires' ident_pattern_token ( ',' ident_pattern_token )* ',' ? ';'

global_value_decl:

| attribute * 'override' optionally_typed_ident ( '=' expression )?

| 'const' optionally_typed_ident '=' expression

hex_float_literal:

| /0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9]+[fh]?)?/

| /0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?/

| /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*([pP][+-]?[0-9]+[fh]?)?/

ident:

| ident_pattern_token

int_literal:

| decimal_int_literal

| hex_int_literal

interpolate_attr:

| '@' 'interpolate' '(' ident_pattern_token ',' ? ')'

| '@' 'interpolate' '(' ident_pattern_token ',' ident_pattern_token ',' ? ')'

invariant_attr:

| '@' 'invariant'

lhs_expression:

| core_lhs_expression component_or_swizzle_specifier ?

| '&' lhs_expression

| '*' lhs_expression

literal:

| bool_literal

| float_literal

| int_literal

member_ident: ident_pattern_token
multiplicative_operator:

| '%'

| '*'

| '/'

must_use_attr:

| '@' 'must_use'

optionally_typed_ident:

| ident ( ':' type_specifier )?

param:

| attribute * ident ':' type_specifier

primary_expression:

| ident template_elaborated_ident.post.ident

| ident template_elaborated_ident.post.ident argument_expression_list

| literal

| '(' expression ')'

relational_expression.post.unary_expression:

| shift_expression.post.unary_expression

| shift_expression.post.unary_expression greater_than unary_expression shift_expression.post.unary_expression

| shift_expression.post.unary_expression greater_than_equal unary_expression shift_expression.post.unary_expression

| shift_expression.post.unary_expression less_than unary_expression shift_expression.post.unary_expression

| shift_expression.post.unary_expression less_than_equal unary_expression shift_expression.post.unary_expression

| shift_expression.post.unary_expression '!=' unary_expression shift_expression.post.unary_expression

| shift_expression.post.unary_expression '==' unary_expression shift_expression.post.unary_expression

shift_expression.post.unary_expression:

| ( multiplicative_operator unary_expression )* ( additive_operator unary_expression ( multiplicative_operator unary_expression )* )*

| shift_left unary_expression

| shift_right unary_expression

statement:

| attribute * 'for' '(' for_init ? ';' expression ? ';' for_update ? ')' compound_statement

| attribute * 'if' expression compound_statement ( 'else' 'if' expression compound_statement )* ( 'else' compound_statement )?

| attribute * 'loop' attribute * '{' statement * ( 'continuing' attribute * '{' statement * ( 'break' 'if' expression ';' )? '}' )? '}'

| attribute * 'switch' expression attribute * '{' switch_clause * '}'

| attribute * 'while' expression compound_statement

| compound_statement

| ident template_elaborated_ident.post.ident argument_expression_list ';'

| variable_or_value_statement ';'

| variable_updating_statement ';'

| assert_statement ';'

| break_statement ';'

| continue_statement ';'

| ';'

| 'discard' ';'

| 'return' expression ? ';'

switch_clause:

| 'case' case_selector ( ',' case_selector )* ',' ? ':' ? compound_statement

| 'default' ':' ? compound_statement

swizzle_name:

| /[rgba]/

| /[rgba][rgba]/

| /[rgba][rgba][rgba]/

| /[rgba][rgba][rgba][rgba]/

| /[xyzw]/

| /[xyzw][xyzw]/

| /[xyzw][xyzw][xyzw]/

| /[xyzw][xyzw][xyzw][xyzw]/

template_arg_expression: expression
template_elaborated_ident.post.ident:

| ( _template_args_start template_arg_expression ( ',' expression )* ',' ? _template_args_end )?

translation_unit:

| global_directive * ( global_decl | global_assert | ';' ) *

translation_unit/0.1/0/0.0:

| global_assert

| global_decl

| ';'

type_specifier:

| ident ( _template_args_start template_arg_expression ( ',' expression )* ',' ? _template_args_end )?

unary_expression:

| primary_expression component_or_swizzle_specifier ?

| '!' unary_expression

| '&' unary_expression

| '*' unary_expression

| '-' unary_expression

| '~' unary_expression

variable_decl:

| 'var' ( _template_args_start expression ( ',' expression )* ',' ? _template_args_end )? optionally_typed_ident

variable_or_value_statement:

| variable_decl

| variable_decl '=' expression

| 'const' optionally_typed_ident '=' expression

| 'let' optionally_typed_ident '=' expression

variable_updating_statement:

| lhs_expression ( '=' | compound_assignment_operator ) expression

| lhs_expression '++'

| lhs_expression '--'

| '_' '=' expression

vertex_attr:

| '@' 'vertex'

workgroup_size_attr:

| '@' 'workgroup_size' '(' expression ',' ? ')'

| '@' 'workgroup_size' '(' expression ',' expression ',' ? ')'

| '@' 'workgroup_size' '(' expression ',' expression ',' expression ',' ? ')'

부록 A: text/wgsl 미디어 타입

인터넷 할당 번호 관리 기구(IANA)는 미디어 타입 레지스트리를 [IANA-MEDIA-TYPES]에 관리합니다.

아래는 WGSL 모듈용 text/wgsl 미디어 타입의 정의입니다. 이 타입은 IANA에 등록되었으며, https://www.iana.org/assignments/media-types/text/wgsl에 게시되어 있습니다.

타입 이름

text

서브타입 이름

wgsl

필수 파라미터

해당 없음

선택적 파라미터

없음

인코딩 고려사항

binary

WGSL은 바이트 순서 표시(BOM)가 없는 UTF-8 인코딩을 사용하는 유니코드 텍스트입니다. § 3 텍스트 구조를 참고하세요.

보안 고려사항:

WebGPU 셰이딩 언어(WGSL)는 WebGPU API의 컨텍스트에서 실행되는 GPU 코드용 프로그래밍 언어입니다. 보안 관련 사항은 [WebGPU] 2.1절 보안 고려사항을 참고하세요. 개인정보 관련 사항은 [WebGPU] 2.2절 개인정보 보호 고려사항을 참고하세요.

상호운용성 고려사항:

WebGPU 구현마다 기능이 다를 수 있으며, 이러한 차이점은 WGSL 프로그램에서 사용할 수 있는 기능에 영향을 줄 수 있습니다. [WebGPU] 3.6절 선택적 기능, 그리고 § 4.1.2 언어 확장을 참고하세요.

향후 WGSL 판에도 이 등록이 적용되는 것으로 동작할 것으로 예상되며, 이에 따라 명세 참조도 적절히 업데이트될 수 있습니다. 이러한 기대는 미디어 타입 등록에서는 이례적이지만, 업계 관행에 부합합니다.

공식 명세:

WebGPU 셰이딩 언어

이 미디어 타입을 사용하는 애플리케이션:

WebGPU 구현. 웹 브라우저도 포함될 것으로 예상됩니다.

프래그먼트 식별자 고려사항

없음

추가 정보:

매직 넘버: 없음

파일 확장자: .wgsl

Macintosh 파일 타입 코드: TEXT

추가 문의를 위한 담당자 & 이메일 주소:

David Neto, dneto@google.com, 또는 WGSL에 기재된 편집자.

용도

COMMON

저자

W3C. WGSL에 기재된 편집자 참고.

변경 관리자

W3C

규범적 참고문헌

[WebGPU] W3C, "WebGPU” W3C Working Draft, 2023년 1월. https://w3.org/TR/webgpu

Webgpu Shading Language W3C, "WebGPU Shading Language" W3C Working Draft, 2023년 1월. https://w3.org/TR/WGSL

적합성

문서 규약

적합성 요구사항은 설명적 주장과 RFC 2119 용어의 조합으로 표현됩니다. 이 문서의 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”과 같은 주요 단어들은 RFC 2119에 설명된 대로 해석되어야 합니다. 단, 가독성을 높이기 위해 이 문서에서는 해당 단어들을 모두 대문자로 표기하지 않습니다.

명시적으로 비규범적임을 표시한 부분, 예시, 참고를 제외하면 이 명세의 모든 텍스트는 규범적입니다. [RFC2119]

이 명세의 예시는 “예를 들어”라는 문구로 시작하거나, class="example"와 같이 규범적 텍스트와 구분되어 있습니다, 다음과 같이:

이것은 정보 제공 목적의 예시입니다.

참고(Informative) 노트는 “참고”라는 단어로 시작하며, class="note"로 규범적 텍스트와 구분되어 있습니다, 다음과 같이:

참고: 이것은 정보 제공 목적의 노트입니다.

적합 알고리즘

알고리즘의 일부로 명령문 형식으로 표현된 요구사항(예: "앞의 공백 문자를 모두 제거한다" 또는 "false를 반환하고 이 단계를 중단한다")은 알고리즘을 도입하는 데 사용된 주요 단어("must", "should", "may" 등)의 의미로 해석되어야 합니다.

알고리즘이나 특정 단계로 표현된 적합성 요구사항은 최종 결과가 동일하기만 하면 어떤 방식으로든 구현될 수 있습니다. 특히, 이 명세에서 정의된 알고리즘은 이해하기 쉽게 작성된 것이며, 성능을 고려한 것이 아닙니다. 구현자는 최적화를 권장합니다.

목차

이 명세에서 정의된 용어

참조로 정의된 용어

참고 문헌

규범적 참고 문헌

[DeRemer1969]
LR(k) 언어를 위한 실용 번역기. 1969년 10월 24일. URL: http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-065.pdf
[ECMASCRIPT]
ECMAScript 언어 명세. URL: https://tc39.es/ecma262/multipage/
[IEEE-754]
IEEE 부동 소수점 산술 표준. 2008년 8월 29일. URL: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933
[Muller2005]
ulp(x)의 정의에 대하여. 2005년 2월. URL: https://inria.hal.science/inria-00070503
[RFC2119]
S. Bradner. RFC에서 요구 사항 수준을 나타내기 위한 주요 단어. 1997년 3월. 최선의 현행 관행. URL: https://datatracker.ietf.org/doc/html/rfc2119
[UAX14]
Robin Leroy. Unicode 줄 바꿈 알고리즘. 2024년 9월 2일. Unicode Standard Annex #14. URL: https://www.unicode.org/reports/tr14/tr14-53.html
[UAX31]
Mark Davis; Robin Leroy. Unicode 식별자 및 문법. 2024년 9월 2일. Unicode Standard Annex #31. URL: https://www.unicode.org/reports/tr31/tr31-41.html
[UnicodeVersion14]
Unicode 표준, 버전 14.0.0. URL: http://www.unicode.org/versions/Unicode14.0.0/
[VanWyk2007]
Eric R. Van Wyk; August C. Schwerdfeger. 확장 가능한 언어 파싱을 위한 컨텍스트 인식 스캐닝. 2007년. URL: https://dl.acm.org/doi/10.1145/1289971.1289983
[VulkanMemoryModel]
Jeff Bolz; et al. Vulkan 메모리 모델. URL: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#memory-model
[WebGPU]
Kai Ninomiya; Brandon Jones; Myles C. Maxfield. WebGPU. 작업 초안. URL: https://w3.org/TR/webgpu

설명 참고 문헌

[CHARMOD-NORM]
Addison Phillips; et al. 월드 와이드 웹을 위한 문자 모델: 문자열 매칭. 2021년 8월 11일. 참고. URL: https://www.w3.org/TR/charmod-norm/
[CSS-TABLES-3]
François Remy; Greg Whitworth; David Baron. CSS 테이블 모듈 레벨 3. 2019년 7월 27일. WD. URL: https://www.w3.org/TR/css-tables-3/
[IANA-MEDIA-TYPES]
미디어 타입. URL: https://www.iana.org/assignments/media-types/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 현행 표준. 현행 표준. URL: https://infra.spec.whatwg.org/
[Jeannerod2013]
Claude-Pierre Jeannerod; Nicolas Louvet; Jean-Michel Muller. 2x2 행렬식의 정확한 계산을 위한 Kahan 알고리즘의 추가 분석. URL: https://www.ams.org/journals/mcom/2013-82-284/S0025-5718-2013-02679-8/S0025-5718-2013-02679-8.pdf
[WASM-CORE-2]
Andreas Rossberg. 웹어셈블리 코어 명세. 2025년 6월 16일. CRD. URL: https://www.w3.org/TR/wasm-core-2/