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은 스칼라 타입을 벡터 타입으로 승격하는 제한적인 기능을 제공합니다. 이는 합성 타입에도 적용됩니다.
셰이더 단계의 작업은 하나 이상의 인보케이션으로 분할되며, 각각은 엔트리 포인트를 약간 다른 조건에서 실행합니다. 셰이더 단계의 인보케이션들은 특정 변수에 대한 접근을 공유합니다:
-
해당 단계의 모든 인보케이션은 셰이더 인터페이스의 리소스를 공유합니다.
-
컴퓨트 셰이더에서 동일한 워크그룹 내 인보케이션들은 워크그룹 주소 공간의 변수를 공유합니다. 다른 워크그룹의 인보케이션들은 해당 변수를 공유하지 않습니다.
하지만 인보케이션은 서로 다른 셰이더 단계 입력 집합, 즉 인보케이션을 구분하는 식별 값을 제공하는 내장 입력을 포함한 입력 집합을 다룹니다. 각 인보케이션은 private 및 function 주소 공간에 있는 변수 형태로 독립적인 메모리 공간을 가집니다.
셰이더 단계 내 인보케이션들은 동시에, 그리고 종종 병렬로 실행됩니다. 셰이더 작성자는 셰이더 단계의 인보케이션 동적 동작에 대한 책임을 집니다:
-
특정 기본 연산(텍스처 샘플링, 컨트롤 배리어 등)의 균일성 요구 사항을 충족
-
공유 변수에 대한 잠재적 충돌적 접근을 조정하여 데이터 레이스를 방지
WGSL은 특정 기능에 대해 여러 동작을 허용할 수 있습니다. 이는 이식성 위험이 될 수 있으며, 구현에 따라 다른 동작이 나타날 수 있습니다. WGSL의 설계는 이러한 경우를 최소화하는 데 목적을 두지만, 현실적 제약 및 다양한 장치에서 높은 성능을 달성하기 위한 목표에 의해 제한됩니다.
행동적 요구사항은 WGSL 프로그램을 처리하거나 실행할 때 구현체가 수행해야 할 작업을 의미합니다. 이는 프로그래머와의 계약에서 구현체가 준수해야 할 의무를 설명합니다. 명세서는 이러한 의무가 명확하지 않을 수 있는 경우에 대해 이를 명시적으로 기술합니다.
1.2. 구문 표기법
아래의 구문 표기법은 WGSL의 구문 문법 규칙을 설명합니다:
-
규칙 양쪽의 이탤릭체 텍스트는 구문 규칙을 나타냅니다.
-
규칙 오른쪽에 있는 단일 따옴표(')로 시작하고 끝나는 굵은 모노스페이스 텍스트는 키워드와 토큰을 나타냅니다.
-
일반 텍스트의 콜론(:)은 구문 규칙을 등록합니다.
-
일반 텍스트의 수직 막대(|)는 대안을 나타냅니다.
-
일반 텍스트의 물음표(?)는 이전 키워드, 토큰, 규칙 또는 그룹이 0회 또는 1회 발생함(선택적임)을 나타냅니다.
-
일반 텍스트의 별표(*)는 이전 키워드, 토큰, 규칙 또는 그룹이 0회 이상 발생함을 나타냅니다.
-
일반 텍스트의 플러스(+)는 이전 키워드, 토큰, 규칙 또는 그룹이 1회 이상 발생함을 나타냅니다.
-
일반 텍스트에서 여는 괄호(()와 닫는 괄호())가 쌍으로 있는 경우, 요소들의 그룹을 나타냅니다.
1.3. 수학 용어 및 표기법
각도:
-
관례적으로, 각도는 라디안 단위로 측정합니다.
-
각도를 측정하는 기준 광선은 원점(0,0)에서 (+∞,0) 방향입니다.
-
θ를 비교 광선과 기준 광선 사이에 이루어지는 각도로 정의합니다. 비교 광선이 반시계 방향으로 이동하면 θ가 증가합니다.
-
완전한 원에는 2π 라디안이 있습니다.
-
예시:
-
각도 0은 원점에서 오른쪽(1,0) 방향을 가리킵니다.
-
각도 2π도 원점에서 오른쪽(1,0) 방향을 가리킵니다.
-
각도 π/4는 원점에서 (1,1) 지점을 가리킵니다.
-
각도 π/2는 원점에서 (0,1) 지점을 가리킵니다.
-
각도 π는 원점에서 (-1,0) 지점을 가리킵니다.
-
각도 (3/2)π는 원점에서 (0,-1) 지점을 가리킵니다.
-
쌍곡각은 전통적인 의미의 각도가 아니라 단위 없는 면적입니다. 구체적으로:
-
x2 - y2 = 1이며 x > 0인 쌍곡선을 고려합니다.
-
R을 원점에서 쌍곡선 위의 임의의 점(x, y)으로 향하는 광선이라고 합니다.
-
a를 R, x축, 그리고 쌍곡선 곡선 자체가 둘러싼 면적의 두 배로 정의합니다.
-
R이 x축 위에 있으면 a는 양수, 아래에 있으면 음수로 간주합니다.
이때 면적 a는 쌍곡각이며, x는 a의 쌍곡코사인, y는 a의 쌍곡사인입니다.
양의 무한대(+∞로 표기)은 모든 실수보다 엄격하게 큰 고유한 값입니다.
음의 무한대(−∞로 표기)은 모든 실수보다 엄격하게 작은 고유한 값입니다.
확장 실수(affinely extended real numbers)은 실수 집합에 +∞, −∞를 포함한 집합입니다. 컴퓨터는 부동소수점 타입을 사용하여 확장 실수를 근사적으로 표현하며, 두 무한대 값도 포함합니다. § 15.7 부동소수점 평가를 참고하세요.
구간은 하한과 상한이 있는 연속된 숫자 집합입니다. 맥락에 따라 정수, 부동소수점, 실수, 또는 확장 실수 집합일 수 있습니다.
-
폐구간 [a,b]은 a ≤ x ≤ b를 만족하는 x의 집합입니다.
-
반개방 구간 [a,b)은 a ≤ x < b를 만족하는 x의 집합입니다.
-
반개방 구간 (a,b]은 a < x ≤ b를 만족하는 x의 집합입니다.
바닥 함수 표현은 확장 실수 x에 대해 정의됩니다:
-
⌊ +∞ ⌋ = +∞
-
⌊ −∞ ⌋ = −∞
-
실수 x에 대해서는, ⌊x⌋ = k이며, k는 k ≤ x < k+1을 만족하는 유일한 정수입니다.
천장 함수 표현은 확장 실수 x에 대해 정의됩니다:
-
⌈ +∞ ⌉ = +∞
-
⌈ −∞ ⌉ = −∞
-
실수 x에 대해서는, ⌈x⌉ = k이며, k-1 < x ≤ k를 만족하는 유일한 정수 k입니다.
절삭 함수는 확장 실수 x에 대해 정의됩니다:
-
truncate(+∞) = +∞
-
truncate(−∞) = −∞
-
실수 x에 대해, 절댓값이 x의 절댓값보다 작거나 같은 가장 가까운 정수를 계산합니다:
-
truncate(x) = ⌊x⌋ (x ≥ 0일 때),
truncate(x) = ⌈x⌉ (x < 0일 때)
-
올림 함수는 양의 정수 k와 n에 대해 다음과 같이 정의합니다:
-
roundUp(k, n) = ⌈n ÷ k⌉ × k
전치는 c개의 열과 r개의 행을 갖는 행렬 A에 대해, r개의 열과 c개의 행을 갖는 행렬 AT로, A의 행을 AT의 열로 복사하여 만듭니다:
-
transpose(A) = AT
-
transpose(A)i,j = Aj,i
열 벡터의 전치는 해당 열 벡터를 1행 행렬로 해석하여 정의합니다. 동일하게, 행 벡터의 전치는 해당 행 벡터를 1열 행렬로 해석하여 정의합니다.
2. WGSL 모듈
WGSL 프로그램은 하나의 WGSL 모듈로 구성됩니다.
모듈은 선택적 디렉티브의 시퀀스 뒤에 모듈 범위 선언 및 단언문이 이어집니다. 모듈은 다음과 같이 구성됩니다:
-
디렉티브: 모듈 수준의 동작 제어를 지정합니다.
-
함수: 실행 동작을 지정합니다.
-
문장: 선언 또는 실행 가능한 동작 단위입니다.
-
리터럴: 순수 수학적 값을 위한 텍스트 표현입니다.
-
변수: 값을 저장하는 메모리에 이름을 제공합니다.
-
상수: 특정 시점에 계산된 값에 이름을 제공합니다.
-
식: 값 집합을 결합하여 결과 값을 만듭니다.
-
타입: 다음을 설명합니다.
-
값의 집합
-
지원되는 식의 제약 조건
-
해당 식의 의미론
-
-
속성: 객체에 추가 정보를 지정합니다. 예:
global_directive * ( global_decl | global_assert | ';'
) *
2.1. 셰이더 라이프사이클
WGSL 프로그램과 그 안에 포함될 수 있는 셰이더의 라이프사이클에는 네 가지 주요 이벤트가 있습니다. 처음 두 개는 WGSL 프로그램을 실행 준비하는 데 사용되는 WebGPU API 메서드에 해당합니다. 마지막 두 개는 셰이더의 실행 시작과 종료입니다.
이벤트는 다음과 같습니다:
-
셰이더 모듈 생성
-
이는 WebGPU
createShaderModule()
메서드가 호출될 때 발생합니다. WGSL 프로그램의 소스 텍스트가 이 시점에 제공됩니다.
-
-
파이프라인 생성
-
이는 WebGPU
createComputePipeline()
메서드 또는 WebGPUcreateRenderPipeline()
메서드가 호출될 때 발생합니다. 이들 메서드는 이전에 생성된 하나 이상의 셰이더 모듈과 기타 구성 정보를 함께 사용합니다. -
지정된 엔트리 포인트의
GPUProgrammableStage
셰이더를 구성하는 코드만 파이프라인 생성 시 고려됩니다. 즉, 엔트리 포인트와 관련 없는 코드는 컴파일 전 효과적으로 제거됩니다. -
참고: 각 셰이더 단계는 별도로 컴파일된 것으로 간주되며, 따라서 모듈의 서로 다른 부분을 포함할 수 있습니다.
-
-
셰이더 실행 시작
-
이는 draw 또는 dispatch command가 GPU에 발행되어 파이프라인 실행을 시작하고, 셰이더 단계 엔트리 포인트 함수를 호출할 때 발생합니다.
-
-
셰이더 실행 종료
이 이벤트들은 다음 때문에 순서가 지정됩니다:
-
데이터 의존성: 셰이더 실행에는 파이프라인이 필요하고, 파이프라인에는 셰이더 모듈이 필요합니다.
-
인과성: 셰이더는 실행을 시작해야 실행을 종료할 수 있습니다.
2.2. 오류
WebGPU 구현체는 두 가지 이유로 셰이더 처리에 실패할 수 있습니다:
-
프로그램 오류 셰이더가 WGSL 또는 WebGPU 명세의 요구 사항을 충족하지 못하면 발생합니다.
-
분류되지 않은 오류 WGSL 및 WebGPU의 모든 요구 사항을 충족하더라도 발생할 수 있습니다. 가능한 원인으로는:
-
셰이더가 너무 복잡하여 구현체의 능력을 초과하지만, 제한으로 쉽게 파악할 수 없는 경우. 셰이더를 단순화하면 문제가 해결될 수 있습니다.
-
WebGPU 구현체의 결함.
-
처리 오류는 셰이더 라이프사이클의 세 단계에서 발생할 수 있습니다:
-
셰이더 생성 오류 셰이더 모듈 생성 시점에 탐지 가능한 오류입니다. 탐지는 WGSL 모듈 소스 텍스트와
createShaderModule
API 메서드에서 사용할 수 있는 기타 정보에만 의존합니다. 명세서에서 프로그램이 반드시 해야 한다고 기술된 사항을 위반하면 일반적으로 셰이더 생성 오류가 발생합니다. -
파이프라인 생성 오류 파이프라인 생성 시점에 탐지 가능한 오류입니다. 탐지는 WGSL 모듈 소스 텍스트와 해당 파이프라인 생성 API 메서드에서 사용할 수 있는 정보에 의존합니다. 이러한 오류는 셰이더의 엔트리 포인트 컴파일 시에만 트리거됩니다.
GPUProgrammableStage
. -
동적 오류 셰이더 실행 중에 발생하는 오류입니다. 이 오류는 탐지 가능할 수도, 불가능할 수도 있습니다.
참고: 예를 들어, 데이터 레이스는 탐지되지 않을 수 있습니다.
각 요구 사항 will은 가능한 한 빨리 확인됩니다. 즉:
-
셰이더 생성 시점에 탐지 가능한 요구 사항을 충족하지 못하면 셰이더 생성 오류가 발생합니다.
-
파이프라인 생성 시점에만 탐지 가능한 요구 사항을 충족하지 못하면 파이프라인 생성 오류가 발생합니다. 이전에 탐지되지 않은 경우에 한함.
맥락에서 명확하지 않은 경우, 이 명세서는 특정 요구 사항을 충족하지 못할 때 셰이더 생성 오류, 파이프라인 생성 오류, 또는 동적 오류 중 어떤 오류가 발생하는지 명시합니다.
오류의 결과는 다음과 같습니다:
-
셰이더 생성 오류 또는 파이프라인 생성 오류가 있는 WGSL 모듈은 파이프라인에 포함되지 않으며 실행되지 않습니다.
-
동적 오류가 발생하면:
2.3. 진단
구현체는 셰이더 모듈 생성 또는 파이프라인 생성 중에 진단을 생성할 수 있습니다. 진단은 구현체가 애플리케이션 작성자를 위해 생성하는 메시지입니다.
특정 조건이 만족되면 진단이 생성되거나 트리거됩니다. 이 조건을 트리거 규칙이라고 합니다. 조건이 만족된 소스 텍스트 내의 위치(지점 또는 범위)를 트리거 위치라고 합니다.
진단은 다음 속성을 가집니다:
심각도는 다음 중 하나이며, 높은 순서대로 나열됩니다:
- 오류
-
진단이 오류임을 의미합니다. 이는 셰이더 생성 오류 또는 파이프라인 생성 오류에 해당합니다.
- 경고
-
진단은 오류는 아니지만, 애플리케이션 개발자가 주목할 만한 이상 현상을 설명합니다.
- 정보
-
진단은 오류나 경고는 아니지만, 개발자가 주목해야 할 중요한 상황을 설명합니다.
- 꺼짐
-
진단이 비활성화되었습니다. 애플리케이션에 전달되지 않습니다.
트리거 규칙의 이름은 다음 중 하나입니다:
-
두 개의 diagnostic_name_token 토큰이 마침표
'.'
(U+002E)로 구분되어 있습니다.
2.3.1. 진단 처리
트리거된 진단은 다음과 같이 처리됩니다:
-
각 진단 D에 대해, D의 트리거 위치를 포함하고 동일한 트리거 규칙을 가진 가장 작은 진단 필터를 찾습니다.
-
해당 필터가 있으면, D에 적용하여 D의 심각도를 갱신합니다.
-
없으면 D는 변경되지 않습니다.
-
-
심각도가 꺼짐인 진단을 버립니다.
-
남은 진단 중 적어도 하나 DI가 정보 심각도를 가지면:
-
동일한 트리거 규칙을 가진 다른 정보 진단은 일부 버려질 수 있으며, 원래 진단 DI만 남길 수 있습니다.
-
-
남은 진단 중 적어도 하나 DW가 경고 심각도를 가지면:
-
남은 진단 중 하나라도 오류 심각도를 가지면:
-
셰이더 모듈 생성 시점에 처리하는 경우, 남은 진단은 WebGPU
messages
멤버에 저장됩니다.GPUCompilationInfo
객체입니다. -
파이프라인 생성 중에 처리하는 경우, 오류 진단은 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 모듈에 진단 필터를 적용할 수 있습니다.
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)는 다음의 경우 충돌합니다:
-
(AR1 = AR2)이고,
-
(TR1 = TR2)이고,
-
(NS1 ≠ NS2)일 때.
참고: 글로벌 진단 필터는 충돌하지 않는 한 여러 개 허용됩니다.
WGSL의 진단 필터는 영향 범위가 완전히 중첩되도록 설계되었습니다. DF1의 영향 범위가 DF2와 겹치면, DF1의 영향 범위가 DF2에 완전히 포함되거나 그 반대입니다.
소스 위치 L와 트리거 규칙 TR에 대한 가장 가까운 포함 진단 필터는, 존재한다면 다음 조건을 만족하는 진단 필터 DF(AR,NS,TR)입니다:
-
L이 영향 범위 AR 내에 포함되고,
-
만약 L이 AR'에도 포함되는 다른 필터 DF'(AR',NS',TR)가 있다면, AR이 AR'에 포함됩니다.
영향 범위가 중첩되므로, 가장 가까운 포함 진단 필터는:
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 모듈을 파싱하려면:
주석을 제거합니다:
첫 번째 주석을 공백 코드 포인트(
U+0020
)로 대체합니다.주석이 더 이상 남지 않을 때까지 반복합니다.
템플릿 리스트를 찾습니다. 알고리즘은 § 3.9 Template Lists를 참고하세요.
전체 텍스트를 파싱하여 translation_unit 문법 규칙과 일치하는지 시도합니다. 파싱은 LALR(1) 파서(토큰 하나 lookahead)를 사용하며 [DeRemer1969], 다음과 같은 커스터마이즈가 적용됩니다:
토큰화는 파싱과 병행되며, 컨텍스트 인식형입니다. 파서가 다음 토큰을 요청할 때:
초기 연속 공백 코드 포인트들을 소비하고 무시합니다.
다음 코드 포인트가 템플릿 리스트의 시작이면, 이를 소비하고 _template_args_start를 반환합니다.
다음 코드 포인트가 템플릿 리스트의 끝이면, 이를 소비하고 _template_args_end를 반환합니다.
그 외의 경우:
토큰 후보란 남은 미소비 코드 포인트의 비어 있지 않은 접두사로 형성된 WGSL 토큰입니다.
반환되는 토큰은 현재 파서 상태에서 유효한 lookahead 토큰이면서 가장 긴 토큰 후보입니다. [VanWyk2007]
다음 경우 셰이더 생성 오류가 발생합니다:
-
전체 소스 텍스트를 유효한 토큰의 유한 시퀀스로 변환할 수 없는 경우
-
translation_unit 문법 규칙이 전체 토큰 시퀀스와 일치하지 않는 경우
3.2. 공백 및 줄바꿈
공백은 유니코드 Pattern_White_Space 속성의 코드 포인트들 중 하나 이상으로 구성된 조합입니다. Pattern_White_Space에 속하는 코드 포인트 집합은 다음과 같습니다:
-
공백 (
U+0020
) -
수평 탭 (
U+0009
) -
줄 바꿈 (
U+000A
) -
수직 탭 (
U+000B
) -
폼 피드 (
U+000C
) -
캐리지 리턴 (
U+000D
) -
다음 줄 (
U+0085
) -
좌에서 우 마크 (
U+200E
) -
우에서 좌 마크 (
U+200F
) -
줄 구분자 (
U+2028
) -
문단 구분자 (
U+2029
)
줄바꿈은 공백 코드 포인트의 연속으로, 줄의 끝을 나타냅니다. UAX14 섹션 6.1 Non-tailorable Line Breaking Rules의 "mandatory break"를 신호하는 공백으로 정의되며, LB4 및 LB5를 참고하세요. 즉, 줄바꿈은 다음 중 하나입니다:
-
줄 바꿈 (
U+000A
) -
수직 탭 (
U+000B
) -
폼 피드 (
U+000C
) -
캐리지 리턴 (
U+000D
)이 줄 바꿈(U+000A
) 뒤에 오지 않을 때 -
캐리지 리턴 (
U+000D
) 다음에 줄 바꿈(U+000A
)이 올 때 -
다음 줄 (
U+0085
) -
줄 구분자 (
U+2028
) -
문단 구분자 (
U+2029
)
참고: 소스 텍스트 위치를 줄 번호로 보고하는 진단은 줄 개수를 셀 때 줄바꿈을 사용해야 합니다.
3.3. 주석
주석은 WGSL 프로그램의 유효성이나 의미에 영향을 주지 않는 텍스트 영역이며, 주석은 토큰을 분리할 수 있습니다. 셰이더 작성자는 프로그램을 문서화하기 위해 주석을 사용할 수 있습니다.
줄 끝 주석은 주석의 한 종류로,
두 코드 포인트 //
(U+002F
와 U+002F
)와 그 뒤에 오는 코드 포인트들로 구성되며,
다음 중 하나가 나오기 전까지입니다:
-
다음 줄바꿈
-
프로그램 끝
블록 주석은 주석의 한 종류로, 다음으로 구성됩니다:
-
두 코드 포인트
/*
(U+002F
와U+002A
) -
그리고 아래 중 하나의 시퀀스:
-
*/
(U+002A
와U+002F
) 또는/*
(U+002F
와U+002A
)를 포함하지 않는 텍스트
-
마지막으로 두 코드 포인트
*/
(U+002A
와U+002F
)
참고: 블록 주석은 중첩될 수 있습니다. 블록 주석은 시작과 끝 텍스트 시퀀스가 반드시 일치해야 하며, 임의의 중첩을 허용하기 때문에 정규표현식으로 인식할 수 없습니다. 이는 정규 언어의 펌핑 렘마(Pumping Lemma)로 인한 결과입니다.
const f = 1.5 ; // 이것은 줄 끝 주석입니다. const g = 2.5 ; /* 이것은 여러 줄에 걸친 블록 주석입니다 블록 주석은 중첩될 수 있습니다. /* 블록 주석은 중첩 가능합니다. */ 모든 블록 주석은 반드시 종료되어야 합니다. */
3.4. 토큰
토큰은 연속된 코드 포인트 시퀀스로 다음 중 하나를 형성합니다:
3.5. 리터럴
리터럴은 다음 중 하나입니다:
3.5.1. 불리언 리터럴
'true'
| 'false'
3.5.2. 숫자 리터럴
숫자 리터럴의 형태는 패턴 매칭을 통해 정의됩니다.
정수 리터럴은 다음과 같습니다:
-
정수는 아래 중 하나로 지정됩니다:
-
0
-
첫 번째 숫자가
0
이 아닌 십진수 숫자의 시퀀스. -
0x
또는0X
뒤에 16진수 숫자 시퀀스.
-
-
그 뒤에 선택적으로
i
또는u
접미사가 올 수 있음.
참고: 0이 아닌 정수 리터럴 앞에 0이 오는 것(예: 012)은 금지되며, 이는 다른 언어에서 0으로 시작하면 8진수로 인식되는 혼동을 방지하기 위함입니다.
/0[iu]?/
| /[1-9][0-9]*[iu]?/
/0[xX][0-9a-fA-F]+[iu]?/
부동소수점 리터럴은 십진수 부동소수점 리터럴 또는 16진수 부동소수점 리터럴입니다.
부동소수점 리터럴은 두 개의 논리적 부분을 가집니다: 분수를 나타내는 유효숫자와 선택적인 지수입니다. 대략적으로, 리터럴의 값은 유효숫자에 기저값을 지수만큼 거듭제곱한 값을 곱한 것입니다. 유효숫자 자리수는 유효숫자라고 하며, 0이 아니거나, 해당 숫자의 왼쪽과 오른쪽에 모두 0이 아닌 유효숫자가 있을 때 유효숫자입니다. 유효숫자는 왼쪽부터 오른쪽으로 센다: N번째 유효숫자는 왼쪽에 N-1개의 유효숫자가 있습니다.
십진수 부동소수점 리터럴은 다음과 같습니다:
-
유효숫자(분수)는 숫자 시퀀스로 지정되며, 중간에 선택적으로 소수점(
.
)이 올 수 있음. 유효숫자는 10진법 분수를 나타냅니다. -
그 뒤에 선택적으로 지수 접미사가 올 수 있는데, 다음으로 구성됨:
-
e
또는E
-
그 뒤에 선택적으로 부호(
+
,-
)가 붙은 십진수로 지정된 지수. -
그 뒤에 선택적으로
f
또는h
접미사가 올 수 있음.
-
-
소수점, 지수,
f
또는h
접미사 중 하나 이상이 반드시 포함되어야 합니다. 아무 것도 없으면 해당 토큰은 정수 리터럴입니다.
/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 ;
-
significand에서 effective_significand를 계산:
-
significand에 유효숫자가 20개 이하이면, effective_significand는 significand와 동일.
-
그 외의 경우:
-
truncated_significand를 significand와 동일하게 하되, 20번째 유효숫자 오른쪽의 모든 숫자를 0으로 대체
-
truncated_significand_next는 significand와 같으나:
-
20번째 유효숫자를 1 증가시키고, 필요시 왼쪽으로 캐리 전파하여 각 숫자가 0~9 범위 내에 있도록 함
-
20번째 유효숫자 오른쪽의 모든 숫자를 0으로 대체
-
-
effective_significand는 truncated_significand 또는 truncated_significand_next 중 하나로 설정(구현 선택)
-
-
-
리터럴의 수학적 값은 effective_significand의 10진 분수 값에, 지수만큼 10을 곱한 값입니다. 지수가 명시되지 않은 경우, 지수는 0으로 간주.
참고: 십진수 유효숫자는 20자리 이후로 잘려, 약 log(10)/log(2)×20 ≈ 66.4 비트가 보존됩니다.
16진수 부동소수점 리터럴은 다음과 같습니다:
-
0x
또는0X
접두사 -
그 뒤에 16진수 숫자 시퀀스로 지정된 유효숫자, 중간에 선택적으로 16진수 소수점(
.
)이 올 수 있음. 유효숫자는 16진법 분수를 나타냄. -
그 뒤에 선택적으로 지수 접미사:
-
p
또는P
-
그 뒤에 선택적으로 부호(
+
,-
)가 붙은 십진수로 지정된 지수. -
그 뒤에 선택적으로
f
또는h
접미사가 올 수 있음.
-
-
16진수 소수점 또는 지수 중 최소 하나는 반드시 포함되어야 합니다. 둘 다 없으면 해당 토큰은 정수 리터럴입니다.
/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]?/
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 ;
-
significand에서 effective_significand를 계산:
-
significand에 유효숫자가 16개 이하이면 effective_significand는 significand와 동일.
-
그 외의 경우:
-
truncated_significand를 significand와 동일하게 하되, 16번째 유효숫자 오른쪽의 모든 숫자를 0으로 대체
-
truncated_significand_next는 significand와 같으나:
-
16번째 유효숫자를 1 증가시키고, 필요시 왼쪽으로 캐리 전파하여 각 숫자가 0~f 범위 내에 있도록 함
-
16번째 유효숫자 오른쪽의 모든 숫자를 0으로 대체
-
-
effective_significand는 truncated_significand 또는 truncated_significand_next 중 하나로 설정(구현 선택)
-
-
-
리터럴의 수학적 값은 effective_significand의 16진 분수 값에, 지수만큼 2를 곱한 값입니다. 지수가 명시되지 않은 경우, 지수는 0으로 간주.
참고: 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 |
셰이더 생성 오류는 다음 경우 발생합니다:
-
i
또는u
접미사가 붙은 정수 리터럴이 대상 타입으로 나타낼 수 없는 경우 -
f
또는h
접미사가 붙은 16진수 부동소수점 리터럴이 오버플로우되거나 대상 타입에 정확히 나타낼 수 없는 경우 -
f
또는h
접미사가 붙은 십진수 부동소수점 리터럴이 대상 타입에서 오버플로우된 경우
참고: 16진수 float 값 0x1.00000001p0은 정확히 표현하려면 33 유효숫자 비트가 필요하지만, f32는 명시적 유효숫자 비트가 23개뿐입니다.
참고: 16진수 float 리터럴에 f
접미사를 붙여 타입을 강제하려면 반드시
이진 지수도 사용해야 합니다. 예시: 0x1p0f
로 작성. 0x1f
는 16진수 정수 리터럴로 취급됨.
3.6. 키워드
키워드는 미리 정의된 언어 개념을 참조하는 토큰입니다. WGSL 키워드 목록은 § 16.1 키워드 요약을 참고하세요.
3.7. 식별자
식별자는 이름으로 사용되는 토큰의 한 종류입니다. 자세한 내용은 § 5 선언과 범위를 참고하세요.
WGSL은 사용 사례를 구분하기 위해 두 개의 문법 비종결(nonterminal)을 사용합니다:
-
ident는 선언된 객체에 이름을 붙일 때 사용됩니다.
-
member_ident는 structure 타입의 멤버에 이름을 붙일 때 사용됩니다.
식별자의 형식은 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
, Кызыл
,
𐰓𐰏𐰇
,
朝焼け
, سلام
, 검정
, שָׁלוֹם
, गुलाबी
,
փիրուզ
.
단, 다음 예외가 있습니다:
/([_\p{XID_Start}][\p{XID_Continue}]+)|([\p{XID_Start}])/u
Unicode Character Database for Unicode Version 14.0.0에는 XID_Start 및 XID_Continue의 모든 유효 코드 포인트의 비공식 목록이 포함되어 있습니다.
참고: 일부 내장 함수의 반환 타입은 이름을 WGSL 소스에서 사용할 수 없는 구조체 타입입니다.
이러한 구조체 타입은 두 개의 언더스코어로 시작하는 이름이 선언된
것처럼 설명됩니다.
결과 값은 새로운 let
또는 var
에 타입 추론을 이용해 저장하거나, 바로 멤버 이름으로 추출할 수 있습니다.
frexp
와 modf
설명의 예시 사용을 참고하세요.
3.7.1. 식별자 비교
두 WGSL 식별자가 동일하다는 것은, 오직 두 식별자가 동일한 코드 포인트 시퀀스로 구성되어 있을 때만입니다.
참고: 이 명세는 비교 목적으로 유니코드 정규화를 허용하지 않습니다. 시각적으로/의미적으로 동일하지만 다른 유니코드 문자 시퀀스를 사용하는 값은 일치하지 않습니다. 콘텐츠 작성자는 값 선택 시 동일한 인코딩 시퀀스를 일관되게 사용하거나 문제가 될 수 있는 문자를 피할 것을 권장합니다. 자세한 내용은 [CHARMOD-NORM] 참고.
참고: 사용자 에이전트는 WGSL 모듈의 의미가 동일 식별자의 모든 인스턴스를 해당 식별자의 동형(homograph)으로 치환할 때 달라질 경우, 개발자에게 경고를 표시해야 합니다. (동형문자(homoglyph)는 코드 포인트 시퀀스가 독자에게 다른 시퀀스와 동일하게 보일 수 있는 경우를 의미합니다. 동형문자 탐지 예시로는 앞 문단에 언급된 변환, 매핑, 매칭 알고리즘 등이 있습니다. 두 코드 포인트 시퀀스는 하나의 시퀀스를 반복적으로 동형문자로 치환하여 다른 시퀀스로 만들 수 있으면 동형문자입니다.)
3.8. 상황에 따라 달라지는 이름
상황에 따라 달라지는 이름은 특정 문법적 맥락에서만 개념에 이름을 붙일 때 사용하는 토큰입니다. 이 토큰의 철자는 식별자와 같을 수 있지만, 해당 토큰은 선언된 객체로 해결되지 않습니다. 이 섹션에서는 상황에 따라 달라지는 이름으로 사용되는 토큰을 나열합니다. 해당 토큰은 키워드나 예약어이면 안 됩니다.
3.8.1. 속성 이름
§ 12 속성 참고.
속성 이름은 다음과 같습니다:
3.8.2. 내장 값 이름
내장 값 이름 토큰은 토큰으로, 내장 값의 이름에 사용됩니다.
§ 13.3.1.1 내장 입력 및 출력를 참고하세요.
내장 값 이름은 다음과 같습니다:
3.8.3. 진단 규칙 이름
진단 이름 토큰은 토큰으로, 진단 트리거 규칙의 이름에 사용됩니다.
§ 2.3 진단을 참고하세요.
미리 정의된 진단 규칙 이름은 다음과 같습니다:
3.8.4. 진단 심각도 제어 이름
유효한 진단 필터 심각도 제어 이름은 § 2.3 진단에 나열되어 있지만, 일반적으로 식별자와 동일한 형식을 가집니다:
진단 필터 심각도 제어 이름은 다음과 같습니다:
3.8.5. 확장 이름
유효한 enable-extension 이름은 § 4.1.1 확장 활성화에 나열되어 있지만, 일반적으로 식별자와 동일한 형식을 가집니다:
enable-extension 이름은 다음과 같습니다:
유효한 언어 확장 이름은 § 4.1.2 언어 확장에 나열되어 있지만, 일반적으로 식별자와 동일한 형식을 가집니다:
언어 확장 이름은 다음과 같습니다:
3.8.6. 보간 타입 이름
보간 타입 이름 토큰은 토큰으로, 보간 타입 (interpolate_type_name)의 이름에 사용됩니다.
§ 13.3.1.4 보간을 참고하세요.
보간 타입 이름은 다음과 같습니다:
3.8.7. 보간 샘플링 이름
보간 샘플링 이름 토큰은 토큰으로, 보간 샘플링의 이름에 사용됩니다.
§ 13.3.1.4 보간을 참고하세요.
보간 샘플링 이름은 다음과 같습니다:
3.8.8. 스위즐 이름
/[rgba]/
| /[rgba][rgba]/
| /[rgba][rgba][rgba]/
| /[rgba][rgba][rgba][rgba]/
| /[xyzw]/
| /[xyzw][xyzw]/
| /[xyzw][xyzw][xyzw]/
| /[xyzw][xyzw][xyzw][xyzw]/
3.9. 템플릿 리스트
템플릿 매개변수화는 일반 개념을 수정하는 매개변수를 지정하는 방법입니다. 템플릿 매개변수화를 작성하려면 일반 개념을 쓴 뒤 템플릿 리스트를 씁니다.
주석과 공백을 무시하면, 템플릿 리스트란 다음과 같습니다:
-
처음
'<'
(U+003C) 코드 포인트, 그리고 -
하나 이상의 쉼표로 구분된 템플릿 매개변수 리스트, 그리고
-
선택적으로 끝에 쉼표, 그리고
-
마지막
'>'
(U+003E) 코드 포인트.
템플릿 매개변수의 형식은 아래 템플릿 리스트 발견 알고리즘에 의해 암시적으로 정의됩니다. 일반적으로 이름, 식, 또는 타입입니다.
참고: 예를 들어 vec3<f32>
는 vec3
라는
일반 개념을 <f32>
라는 템플릿 리스트(매개변수 하나, f32 타입)로 수정한 매개변수화입니다.
vec3<f32>
는 구체적인 벡터 타입을 뜻합니다.
참고: 예를 들어 var<storage,read_write>
는 일반
var
개념을 storage
, read_write
템플릿 매개변수로 수정합니다.
array<vec4<f32>>
는 두 번의 템플릿 매개변수화가 있습니다:
-
vec4<f32>
는 일반vec4
개념을f32
템플릿 매개변수로 수정합니다. -
array<vec4<f32>>
는 일반array
개념을vec4<f32>
템플릿 매개변수로 수정합니다.
템플릿 리스트를 구분하는 '<'
(U+003C)와 '>'
(U+003E) 코드 포인트는 다음에도 사용됩니다:
구문적 모호함은 템플릿 리스트 쪽으로 해결됩니다:
-
이후 단계의 토큰화에서는, 템플릿 리스트의 초기
'<'
(U+003C)는 _template_args_start 토큰으로, 마지막'>'
(U+003E)는 _template_args_end 토큰으로 매핑됩니다.
템플릿 리스트 발견 알고리즘은 아래와 같습니다. 다음과 같은 가정과 특성을 사용합니다:
-
식에는
';'
(U+003B),'{'
(U+007B),':'
(U+003A) 코드 포인트가 포함되지 않습니다. -
식에는 대입문이 포함되지 않습니다.
-
'='
(U+003D) 코드 포인트는 비교 연산('<='
,'>='
,'=='
,'!='
)의 일부로만 나타납니다. 그 외의 경우'='
는 대입문의 일부입니다. -
템플릿 리스트 구분자는 괄호 '(...)'와 배열 인덱싱 '[...]'로 구성된 중첩 식을 존중합니다. 템플릿 리스트의 시작과 끝은 동일한 중첩 레벨에 있어야 합니다.
알고리즘: 템플릿 리스트 발견입력: 프로그램 소스 텍스트.
레코드 타입:
UnclosedCandidate는 다음을 포함하는 레코드 타입입니다:
position: 소스 텍스트 내 위치
depth: position 위치의 식 중첩 깊이(정수)
TemplateList는 다음을 포함하는 레코드 타입입니다:
start_position: 이 템플릿 리스트를 시작하는
'<'
(U+003C) 코드 포인트의 소스 위치end_position: 이 템플릿 리스트를 끝내는
'>'
(U+003E) 코드 포인트의 소스 위치출력: DiscoveredTemplateLists: TemplateList 레코드의 리스트.
절차:
DiscoveredTemplateLists를 빈 리스트로 초기화합니다.
Pending 변수를 UnclosedCandidate 레코드의 빈 스택으로 초기화합니다.
CurrentPosition 정수 변수를 0으로 초기화합니다. 이 변수는 현재 검사 중인 코드 포인트 위치(소스 텍스트 시작 이후 코드 포인트 수)를 나타냅니다.
이 변수는 알고리즘 실행 중 텍스트를 앞으로 진행하며, 텍스트 끝에 도달하면 즉시 알고리즘을 종료하고 DiscoveredTemplateLists를 반환합니다.
NestingDepth 정수 변수를 0으로 초기화합니다.
다음 단계들을 반복합니다:
ident_pattern_token이 CurrentPosition의 텍스트와 일치하면:
CurrentPosition을 ident_pattern_token 이후로 이동합니다.
공백과 주석이 있다면 CurrentPosition을 그 이후로 이동합니다.
'<'
(U+003C)가 CurrentPosition에 있으면:
참고: 이 코드 포인트는 템플릿 리스트 시작 후보입니다. 나중에
'>'
와 매칭할 수 있도록 상태를 저장합니다.UnclosedCandidate(position=CurrentPosition,depth=NestingDepth)를 Pending 스택에 푸시합니다.
CurrentPosition을 다음 코드 포인트로 이동합니다.
'<'
가 CurrentPosition에 또 있으면:
참고: 가정 1에 따라 어떤 템플릿 매개변수도
'<'
로 시작하지 않으므로, 이전 코드 포인트는 템플릿 리스트 시작이 될 수 없습니다. 따라서 현재와 이전 코드 포인트는'<<'
연산자입니다.Pending 스택의 top을 팝합니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
'='
가 CurrentPosition에 있으면:
참고: 가정 1에 따라 어떤 템플릿 매개변수도
'='
로 시작하지 않으므로, 이전 코드 포인트는 템플릿 리스트 시작이 될 수 없습니다. 따라서 현재와 이전 코드 포인트는'<='
비교 연산자입니다.'='
코드 포인트를 건너뛰어 나중에 대입으로 오인되지 않도록 합니다.Pending 스택의 top을 팝합니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
다음 반복을 시작합니다.
'>'
(U+003E)가 CurrentPosition에 있으면:
참고: 이 코드 포인트는 템플릿 리스트 끝 후보입니다.
Pending이 비어 있지 않으면 top을 T로 두고 T.depth가 NestingDepth와 같다면:
참고: 이 코드 포인트는 T에 기록된 템플릿 리스트를 끝냅니다.
TemplateList(start_position=T.position, end_position=CurrentPosition)를 DiscoveredTemplateLists에 추가합니다.
T를 Pending 스택에서 팝합니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
그 외에는 이 코드 포인트가 템플릿 리스트를 끝내지 않습니다:
CurrentPosition을 다음 코드 포인트로 이동합니다.
'='
가 CurrentPosition에 있으면:
참고: 현재와 이전 코드 포인트는
'>='
비교 연산자입니다.'='
코드 포인트를 건너뛰어 나중에 대입으로 오인되지 않도록 합니다.CurrentPosition을 다음 코드 포인트로 이동합니다.
다음 반복을 시작합니다.
'('
(U+0028) 또는'['
(U+005B)가 CurrentPosition에 있으면:
참고: 중첩 식 진입.
NestingDepth에 1을 더합니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
')'
(U+0029) 또는']'
(U+005D)가 CurrentPosition에 있으면:
참고: 중첩 식 탈출.
Pending 스택의 top의 depth가 NestingDepth 미만이 될 때까지 팝하거나, 스택이 비울 때까지 팝합니다.
NestingDepth를 0 또는 NestingDepth − 1 중 더 큰 값으로 설정합니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
'!'
(U+0021)가 CurrentPosition에 있으면:
CurrentPosition을 다음 코드 포인트로 이동합니다.
'='
가 CurrentPosition에 있으면:
참고: 현재와 이전 코드 포인트는
'!='
비교 연산자입니다.'='
코드 포인트를 건너뛰어 나중에 대입으로 오인되지 않도록 합니다.CurrentPosition을 다음 코드 포인트로 이동합니다.
다음 반복을 시작합니다.
'='
(U+003D)가 CurrentPosition에 있으면:
CurrentPosition을 다음 코드 포인트로 이동합니다.
'='
가 CurrentPosition에 있으면:
참고: 현재와 이전 코드 포인트는
'=='
비교 연산자입니다.'='
코드 포인트를 건너뛰어 나중에 대입으로 오인되지 않도록 합니다.CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
참고: 이 코드 포인트는 대입문의 일부로, 식에서 나올 수 없으므로 템플릿 리스트에도 나올 수 없습니다. 미결 후보를 모두 제거합니다.
NestingDepth를 0으로 설정합니다.
Pending 스택을 모두 비웁니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
';'
(U+003B),'{'
(U+007B),':'
(U+003A)가 CurrentPosition에 있으면:
참고: 식 중에는 나올 수 없으므로 템플릿 리스트에도 나올 수 없습니다. 미결 후보를 모두 제거합니다.
NestingDepth를 0으로 설정합니다.
Pending 스택을 모두 비웁니다.
CurrentPosition을 다음 코드 포인트로 이동하고 다음 반복을 시작합니다.
'&&'
또는'||'
가 CurrentPosition의 텍스트와 일치하면:
참고: 이들은 비교 연산보다 우선순위가 낮은 연산자입니다. 현재 식 레벨의 미결 후보를 모두 제거합니다.
참고: 이 규칙으로
a<b || c>d
는 템플릿 리스트 대신 두 비교의 단락 논리합으로 인식됩니다.Pending 스택의 top의 depth가 NestingDepth 미만이 될 때까지 팝하거나, 스택이 비울 때까지 팝합니다.
CurrentPosition을 두 코드 포인트 이후로 이동하고 다음 반복을 시작합니다.
CurrentPosition을 현재 코드 포인트 이후로 이동합니다.
-
UnclosedCandidate에 다음 필드를 추가합니다:
-
parameters: 템플릿 매개변수의 소스 범위 리스트.
-
parameter_start_position: 소스 위치.
-
-
TemplateList에 다음 필드를 추가합니다:
-
parameters: 템플릿 매개변수의 소스 범위 리스트.
-
-
새로운 UnclosedCandidate를 Pending 스택에 푸시할 때:
-
parameters 필드를 빈 리스트로 설정.
-
parameter_start_position을 CurrentPosition 다음 코드 포인트로 설정.
-
-
TemplateList TL을 DiscoveredTemplateLists에 추가할 때:
-
원래 알고리즘처럼 Pending 스택의 top을 T로 둡니다.
-
T의 parameter_start_position에서 CurrentPosition−1까지의 소스 범위를 T.parameters에 푸시합니다.
-
원래 알고리즘처럼 TL을 준비합니다.
-
TL.parameters를 T.parameters로 설정합니다.
-
-
루프의 마지막, 현재 코드 포인트를 넘기기 전에 다음을 체크합니다:
-
'
,
' (U+002C)가 CurrentPosition에 있고 Pending이 비어 있지 않다면:-
Pending 스택의 top을 T로 둡니다.
-
T.parameter_start_position에서 CurrentPosition−1까지의 소스 범위를 T.parameters에 푸시합니다.
-
T.parameter_start_position을 CurrentPosition+1로 설정
-
-
참고: 알고리즘은 리터럴을 명시적으로 건너뜁니다. 왜냐하면 일부 숫자 리터럴은 마지막에 문자가 올 수
있는데, 예를 들어 1.0f
처럼 끝나는 f
를 ident_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<<C
는 B
다음에 왼쪽 시프트 연산자 '<<'
와 C
가 이어진 것으로 파싱됩니다.
템플릿 발견 알고리즘은 B
를 보고 '<
'(U+003C)를 본 다음, 그 뒤의 '<
'(U+003C)가 템플릿 인수
시작이 될 수 없음을 알게 되어,
B
바로 뒤의 '<
'는 템플릿 리스트 시작이 아닙니다.
처음 '<'
와 마지막 '>'
만 템플릿 리스트 구분자이며, 매개변수는 B<<C
입니다.
참고: A<B<=C>
도 위와 비슷하게 분석되어,
B<=C
는 B
다음에 작거나 같은 연산자 '<='
와
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_args_start template_arg_comma_list _template_args_end
template_arg_expression ( ','
template_arg_expression ) * ','
?
4. 지시문
지시문은 토큰 시퀀스로, WGSL 프로그램이 WebGPU 구현에서 처리되는 방식을 수정합니다.
지시문은 선택 사항입니다. 만약 존재한다면, 모든 지시문은 반드시 모든 선언 또는 const 단언문 이전에 나타나야 합니다.
4.1. 확장
WGSL은 시간이 지남에 따라 발전할 것으로 예상됩니다.
확장은 WGSL 명세의 일관된 수정 집합을 명명하여 그룹화한 것으로, 다음과 같은 내용을 포함할 수 있습니다:
-
새로운 문법을 통해 새로운 개념과 동작을 추가, 예를 들어:
-
선언, 문(statement), 속성(attribute), 내장 함수(built-in function)
-
-
현행 표준 또는 기존에 발표된 확장에서의 제한 사항 제거
-
허용되는 동작의 범위를 줄이기 위한 문법
-
프로그램의 일부에서 사용할 수 있는 기능을 제한하는 문법
-
확장이 기존 명세 및 다른 확장과 어떻게 상호작용하는지에 대한 설명
가상적으로, 확장은 다음을 할 수 있습니다:
-
다양한 비트 너비의 정수 등 숫자 스칼라 타입 추가
-
부동 소수점 반올림 모드를 제한하는 문법 추가
-
셰이더에서 atomic 타입을 사용하지 않음을 신호하는 문법 추가
-
새로운 종류의 문(statement) 추가
-
새로운 내장 함수 추가
-
셰이더 호출 방식 제한을 위한 문법 추가
-
새로운 셰이더 단계 추가
확장에는 두 종류가 있습니다: enable-extensions와 language extensions입니다.
4.1.1. Enable Extension
enable-extension은 확장으로, 다음의 조건을 모두 만족해야 기능을 사용할 수 있습니다:
-
구현체가 해당 기능을 지원할 것
-
셰이더가 enable directive를 통해 명시적으로 요청할 것
-
해당 WebGPU
GPUFeatureName
이GPUDevice
를 생성할 때 요청된 필수 기능 중 하나일 것
Enable-extension은 하드웨어 기능 중 보편적으로 제공되지 않는 부분을 노출하는 것을 목적으로 합니다.
enable directive는 하나 이상의 enable-extension 기능을 활성화하는 지시문입니다. 만약 구현체가 나열된 모든 enable-extension을 지원하지 않으면 셰이더 생성 오류가 발생합니다.
'enable'
enable_extension_list ';'
다른 지시문과 마찬가지로, enable directive가 존재한다면, 반드시 모든 선언과 const 단언문 이전에 나타나야 합니다. 확장 이름은 식별자가 아닙니다: 선언으로 해석되지 않습니다.
유효한 enable-extension 목록은 다음 표에 나와 있습니다.
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 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'
language_extension_list ';'
language_extension_name ( ','
language_extension_name ) * ','
?
다른 지시문과 마찬가지로, requires-directive가 존재한다면, 모든 선언과 const 단언문 이전에 나타나야 합니다. 확장 이름은 식별자가 아닙니다: 선언으로 해석되지 않습니다.
WGSL 언어 확장 | 설명 |
---|---|
readonly_and_readwrite_storage_textures | read 및 read_write access mode를 storage texture에 사용할 수 있습니다. 또한 textureBarrier 내장 함수가 추가됩니다. |
packed_4x8_integer_dot_product | 32비트 정수 스칼라에 4-컴포넌트 8비트 정수 벡터를 패킹하여, dot4U8Packed 및 dot4I8Packed 내장 함수의 도트 곱 연산에 입력으로 사용할 수 있습니다. 또한 pack4xI8, pack4xU8, pack4xI8Clamp, pack4xU8Clamp, unpack4xI8, unpack4xU8 내장 함수로 4-컴포넌트 8비트 정수 벡터를 패킹/언패킹할 수 있습니다. |
unrestricted_pointer_parameters |
제한 사항 중에서 사용자 정의 함수에 대한 다음 제한을
제거합니다:
사용자 정의 함수에서 포인터 타입 매개변수는 반드시 다음 주소 공간 중 하나여야 합니다: 사용자 정의 함수의 포인터 타입 인자는 그 root identifier와 동일한 memory view를 가져야 합니다.
|
pointer_composite_access |
복합 값 분해 표현식에서 루트 표현이 포인터인 경우 참조를 반환하는 것을
지원합니다.
예를 들어, 마찬가지로, |
참고: WGSL은 시간이 지남에 따라 당시에 일반적으로 지원되는 언어 확장 기능을 모두 포함하는 언어 확장을 정의할 의도를 가지고 있습니다. requires-directive에서는 이러한 확장을 모든 공통 기능을 나열하는 약어로 사용할 수 있습니다. 이는 점진적으로 기능 집합이 증가하는 것을 나타내며, 일종의 언어 버전처럼 볼 수 있습니다.
4.2. 글로벌 진단 필터
글로벌 진단 필터는 영향 범위가 WGSL 모듈 전체에 해당하는 진단
필터입니다.
지시문이므로, 모든 모듈 범위 선언 이전에 나타납니다.
속성 형태와 동일하게 작성되지만 앞의 @
(U+0040) 코드 포인트 없이, 마지막에 세미콜론을 붙입니다.
'diagnostic'
diagnostic_control ';'
5. 선언과 범위
선언은 식별자와 아래 객체 중 하나를 연결합니다:
즉, 선언은 객체에 대한 이름을 도입합니다.
선언이 프로그램 소스에 나타나지만 다른 선언의 본문 밖에 있다면 모듈 범위에 있습니다.
함수 선언은 모듈 범위에 나타납니다. 함수 선언은 형식 매개변수 선언을 포함할 수 있고, 변수와 값 선언을 본문 내부에 포함할 수 있습니다. 이러한 선언들은 모듈 범위에 있지 않습니다.
참고: 다른 선언을 포함할 수 있는 선언은 함수 선언만 있습니다.
일부 객체는 WebGPU 구현에서 제공되며, WGSL 모듈 소스가 시작되기 전에 선언된 것으로 간주됩니다. 이러한 객체를 사전 선언됨이라 합니다. 예를 들어, WGSL은 다음을 사전 선언합니다:
-
array
,ptr
,texture_2d
와 같은 내장 타입 생성자, -
열거자 (예: read_write, workgroup, rgba8unorm)
범위란 선언된 식별자가 연관된 객체를 가리킬 수 있는 프로그램 소스 위치 집합입니다. 이러한 위치에서 식별자가 해당 선언의 범위 내에 있음이라고 합니다.
선언이 나타나는 위치에 따라 범위가 결정됩니다:
-
사전 선언 객체와 모듈 범위에 선언된 객체는 프로그램 전체에서 범위 내입니다.
-
사용자가 선언한 함수의 각 형식 매개변수는 해당 함수 본문 전체에서 범위 내입니다. 자세한 내용은 § 11.1 사용자 정의 함수 선언을 참고하세요.
-
그 외의 경우, 범위는 선언 끝 바로 뒤에서 시작하는 텍스트 구간입니다. 자세한 내용은 § 7 변수와 값 선언을 참고하세요.
같은 WGSL 소스 프로그램에서 두 선언은 동시에 다음을 허용하지 않아야 합니다:
-
동일한 식별자 이름을 도입하거나,
-
동일한 범위 끝(end-of-scope)을 갖는 경우
참고: 사전 선언 객체는 WGSL 소스 내에 선언이 없습니다. 따라서 사용자가 모듈 범위나 함수 내부에서 같은 이름의 선언을 할 수 있습니다.
식별자는 문법적 맥락에 따라 다음과 같이 사용됩니다:
-
ident 문법 요소와 일치하는 토큰은:
-
선언에서 객체 이름으로 사용되거나,
-
다른 곳에서 선언된 객체를 나타내는 이름으로 사용됨. 이것이 일반적입니다.
-
-
member_ident 문법 요소와 일치하는 토큰은:
-
구조체 값의 멤버를 나타내는 이름으로, 또는 구조체 멤버 참조를 나타내는 이름으로 사용됨. 자세한 내용은 § 8.5.4 구조체 접근 표현식 참고.
ident 토큰이 다른 곳에 선언된 객체를 나타내는 이름으로 사용될 때, 반드시 어떤 선언의 범위 내에 있어야 합니다. 식별자 토큰이 가리키는 객체는 다음과 같이 결정됩니다:
-
토큰이 하나 이상의 모듈 범위가 아닌(non-module-scope) 선언의 범위 내에 있다면, 그 중 가장 가까운 선언의 객체를 나타냅니다.
참고: 가장 가까운 선언은 식별자 토큰 앞에 나타납니다.
-
그 외에, 해당 이름을 가진 모듈 범위 선언이 있다면, 토큰은 그 객체를 나타냅니다.
참고: 모듈 범위 선언은 식별자 토큰 앞이나 뒤에 올 수 있습니다.
-
그 외에, 해당 이름을 가진 사전 선언 객체가 있다면, 그 객체를 나타냅니다.
위 알고리즘으로 식별자를 선언에 대응시킬 때, 식별자가 해당 선언으로 해석됨이라고 합니다. 또한 식별자가 선언된 객체로 해석됨이라고도 합니다.
모든 모듈 범위 선언이 재귀적인 경우 셰이더 생성 오류입니다. 즉, 선언들 사이에 순환(cycle)이 존재하면 안 됩니다:
다음과 같은 방향 그래프를 생각합니다:
각 노드는 선언 D에 해당합니다.
선언 D의 정의가 T로 해석되는 식별자를 언급하면 D에서 T로의 엣지가 있습니다.
이 그래프에는 순환이 없어야 합니다.
참고: 함수 본문은 함수 선언의 일부이므로, 함수는 직접적 또는 간접적으로 재귀적이어서는 안 됩니다.
참고: 모듈 범위가 아닌(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 ;
// 이 선언은 사전 선언된 'min' 내장 함수를 가립니다. // 이 선언은 모듈 범위에 있으므로 전체 소스에서 범위 내입니다. // 내장 함수는 더이상 접근할 수 없습니다. fn min () -> u32{ return 0 ; } const rgba8unorm= 12 ; // 이 선언은 사전 선언된 'rgba8unorm' 열거자를 가립니다.
6. 타입
프로그램은 값을 계산합니다.
WGSL에서 타입은 값의 집합이며, 각 값은 정확히 하나의 타입에 속합니다. 값의 타입은 해당 값에 대해 수행할 수 있는 연산의 문법과 의미를 결정합니다.
예를 들어, 수학적 숫자 1은 WGSL에서 다음과 같은 개별 값에 해당합니다:
-
32비트 부호 있는 정수 값
1i
, -
32비트 부호 없는 정수 값
1u
, -
32비트 부동 소수점 값
1.0f
, -
f16 확장이 활성화된 경우 16비트 부동 소수점 값
1.0h
, -
AbstractInt 값 1,
-
AbstractFloat 값 1.0
WGSL은 이 값들을 서로 다르게 취급하는데, 이는 기계적 표현과 연산이 다르기 때문입니다.
타입은 사전 선언됨이거나, WGSL 소스에서 선언을 통해 생성됩니다.
일부 타입은 템플릿
파라미터화로 표현됩니다.
타입 생성자는 사전 선언된 객체로, 템플릿 리스트와 결합될 때 타입을 나타냅니다.
예를 들어, atomic<u32>
타입은 타입 생성자 atomic
과 템플릿 리스트 <u32>
를 결합한 것입니다.
타입의 개념과 WGSL에서 해당 타입을 나타내는 문법을 구분합니다. 많은 경우 이 명세에서 타입의 표기법은 WGSL 문법과 동일합니다. 예를 들어:
-
32비트 부호 없는 정수 값 집합은 이 명세에서
u32
라고 표기하며, 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)입니다. 타입 규칙은 구문적 구절의 정적 컨텍스트가 해당 구절에 포함된 표현식의 정적 타입을 어떻게 결정하는지 설명합니다. 타입 규칙은 두 부분으로 구성됩니다:
-
결론.
-
전제 조건, 다음을 포함합니다:
-
표현식의 경우:
-
부분 표현식의 타입 단언(부분 표현식이 있다면). 각각은 직접 만족되거나, 가능한 자동 변환으로 만족될 수 있습니다(§ 6.1.2 변환 순위 참고). WGSL은 프로그램의 정적 타입을 결정하기 위해 필요한 모든 상수 표현식을 평가합니다.
-
표현식이 문(statement)에서 어떻게 사용되는지.
-
-
문의 경우:
-
문의 구문적 형식과,
-
문의 최상위 표현식에 대한 타입 단언.
-
-
다른 도식적 매개변수에 대한 조건(있다면).
-
옵션으로, 기타 정적 컨텍스트.
-
타입 규칙은 타입 파라미터를 전제 조건이나 결론에 포함할 수 있습니다. 타입 규칙의 결론이나 전제 조건에 타입 파라미터가 있으면 파라미터화됨이라고 하며, 그렇지 않으면 완전히 구체화됨이라 합니다. 파라미터화된 규칙에서 각 타입 파라미터에 대해 타입을 대입(substitute)하여 완전히 구체화된 타입 규칙을 만들 수 있습니다. 규칙의 타입 파라미터에 타입을 할당하는 작업을 치환(substitution)이라고 합니다.
예를 들어, 논리 부정 (!
e 형태의 표현식)에 대한 타입 규칙은 다음과 같습니다:
전제 조건 | 결론 |
---|---|
e: T T은 bool 또는 vecN<bool> | ! e: T
|
이 규칙은 타입 파라미터 T를 포함하므로 파라미터화된 규칙입니다.
T는 bool, vec2<bool>
,
vec3<bool>
, vec4<bool>
중 하나의 타입을 나타낼 수 있습니다.
T를 vec3<bool>
로 치환하면 아래와 같은 완전히 구체화된 타입 규칙이 나옵니다:
전제 조건 | 결론 |
---|---|
e: vec3<bool> | ! e: vec3<bool>
|
파라미터화된 규칙에서 치환을 통해 얻을 수 있는 각 완전히 구체화된 규칙을 오버로드라고 합니다. 예를 들어, 불리언 부정 규칙에는 네 가지 오버로드가 있는데, 이는 타입 파라미터 T에 할당할 수 있는 네 가지 타입이 있기 때문입니다.
참고: 즉, 파라미터화된 타입 규칙은 여러 완전히 구체화된 타입 규칙의 패턴을 제공하며, 각각은 파라미터화된 규칙에 다른 치환을 적용해서 얻어집니다.
타입 규칙이 구문적 구절에 적용됨이란 다음 경우를 의미합니다:
-
규칙의 결론이 구문적 구절의 올바른 파싱과 일치하고,
-
규칙의 전제 조건이 만족됨.
파라미터화된 타입 규칙은 어떤 표현식에 대해 치환이 존재하여, 완전히 구체화된 타입 규칙을 만들고, 그 규칙이 해당 표현식에 적용될 때 적용된다고 합니다.
예를 들어, 1u+2u
표현식을 생각해 봅시다.
이 표현식에는 두 개의 리터럴 부분 표현식 1u
, 2u
가 있고, 둘 다 u32
타입입니다.
최상위 표현식은 덧셈입니다.
§ 8.7 산술 표현식의 규칙을 참고하면, 덧셈에 대한 타입 규칙이 이 표현식에 적용됩니다. 그 이유는:
-
1u+2u
는 e1+e2 형태의 파싱과 일치하며, e1은1u
, e2는2u
를 나타냅니다. -
e1는 u32 타입이고,
-
e2도 u32 타입이며,
-
타입 파라미터 T에 u32를 치환할 수 있으므로, 전체 표현식에 적용되는 완전히 구체화된 규칙을 얻을 수 있습니다.
구문적 구절을 분석할 때 세 가지 경우가 발생할 수 있습니다:
-
적용 가능한 타입 규칙이 없음: 타입 오류가 발생합니다.
-
적용 가능한 완전히 구체화된 타입 규칙이 정확히 하나 있는 경우, 해당 규칙의 결론이 단언되어 표현식의 정적 타입이 결정됩니다.
-
적용 가능한 타입 규칙이 둘 이상 있음: 즉, 둘 이상의 오버로드의 전제 조건이 만족됨. 이 경우 § 6.1.3 오버로드 해결의 절차를 사용합니다.
위 예시에서 1u+2u
표현식에는 하나의 타입 규칙만 적용되므로, 타입 검사는 해당 규칙의 결론인 1u+2u
의 타입이 u32임을 받아들입니다.
WGSL 소스 프로그램이 정상적으로 타입이 지정됨(well-typed)이란 다음의 경우입니다:
-
프로그램의 각 표현식에 대해 타입 규칙을 적용하여 정적 타입을 결정할 수 있고,
-
각 문(statement)의 타입 요구 사항이 만족됨.
그렇지 않으면 타입 오류가 발생하며, 해당 소스 프로그램은 올바른 WGSL 모듈이 아닙니다.
WGSL은 정적 타입 언어인데, 이는 WGSL 모듈에 대해 타입 검사를 수행하면 성공하거나 타입 오류를 발견하며, 오직 프로그램 소스 텍스트만 검사하면 되기 때문입니다.
6.1.1. 타입 규칙 표
WGSL 타입 규칙은 표현식에 대해 타입 규칙 표로 구성되어 있으며, 각 타입 규칙마다 한 행이 있습니다.
표현식의 의미론은 해당 표현식을 평가한 효과이며, 주로 결과 값을 생성하는 것입니다. 적용되는 타입 규칙의 설명 열에 해당 표현식의 의미론이 명시되어 있습니다. 의미론은 보통 타입 규칙 파라미터의 값, 그리고 부분 표현식의 가정된 값에 따라 달라집니다. 때로 표현식의 의미론에는 결과 값 생성 외의 효과(부분 표현식의 결과값이 아닌 효과 등)도 포함될 수 있습니다.
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가 타입 규칙의 전제 조건으로 사용될 때, 다음 중 하나면 만족됩니다:
-
e가 이미 타입 T이면,
-
e가 타입 S이고, 타입 S가 아래 정의에 따라 타입 T로 자동 변환 가능할 때
이 규칙은 아래 표에 정의된 타입 쌍에 대한 ConversionRank 함수로 공식화됩니다. ConversionRank 함수는 한 타입(Src) 값을 다른 타입(Dest)으로 자동 변환하는 우선순위와 가능성을 나타냅니다. 순위가 낮을수록 더 바람직합니다.
가능한 자동 변환은 Src 타입의 값을 Dest 타입으로 변환하는 것으로, ConversionRank(Src,Dest)가 유한할 때 허용됩니다. 이러한 변환은 § 15.7 부동 소수점 평가에 설명된 제한을 제외하면 값 보존적입니다.
참고: 자동 변환은 두 가지 상황에서만 발생합니다. 첫째, 상수 표현식을 GPU에서 사용할 수 있는 해당 타입의 숫자 값으로 변환할 때. 둘째, 메모리 참조에서 값을 로드할 때 발생합니다.
참고: 변환 순위가 무한대인 변환은 불가능(허용되지 않음)입니다.
참고: 변환이 수행되지 않으면 변환 순위는 0입니다.
Src | Dest | ConversionRank(Src,Dest) | 설명 |
---|---|---|---|
T | T | 0 | 동일(Identity). 변환 없음. |
ref<AS,T,AM> 주소 공간 AS, 그리고 access mode AM이 read 또는 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)이며, 다음이 성립할 때입니다:
-
T
가 구체 타입이고, -
T
가 참조 타입이 아니며, -
ConversionRank(
S
,T
)가 유한하며, -
다른 참조가 아닌 타입
T2
에 대해 ConversionRank(S
,T2
) > ConversionRank(S
,T
)임.
값 e
가 타입 T
일 때, 그 구체화(concretization)란
e
에 대해 T
에서 해당 구체화 타입으로 가능한 변환을 적용한 값을 의미합니다.
참고: f32로의 변환이 항상 f16보다 우선되므로, 자동 변환은 f16을 생성하려면 반드시 모듈에서 확장(extension)이 활성화되어 있어야 합니다.
6.1.3. 오버로드 해결
둘 이상의 타입 규칙이 구문적 구절에 적용될 때는 어떤 규칙을 적용할지 결정하기 위한 동점(타이브레이크) 절차가 사용됩니다. 이 절차를 오버로드 해결이라 하며, 타입 검사가 이미 부분 표현식에 대한 정적 타입을 찾은 상태를 전제로 합니다.
구문적 구절 P와, P에 적용되는 모든 타입 규칙을 생각해 봅시다. 오버로드 해결 알고리즘은 이러한 타입 규칙을 오버로드 후보라 부릅니다. 각 후보에 대해 다음이 성립합니다:
P에 대한 오버로드 해결은 다음과 같이 진행되며, 가장 우선되는 오버로드 후보 하나를 찾는 것이 목표입니다:
-
각 후보 C에 대해, 구문적 구절의 부분 표현식에 대한 변환 순위를 열거합니다. 후보의 전제 조건이 만족되었으므로, P의 i번째 부분 표현식에 대해:
-
정적 타입이 계산됨.
-
해당 표현식의 정적 타입에서 전제 조건의 타입 단언이 요구하는 타입으로 가능한 자동 변환이 존재함. C.R(i)는 그 변환의 ConversionRank입니다.
-
-
부분 표현식 중 하나가 가능한 자동 변환 이후 추상 타입으로 해석되고, 다른 후보의 부분 표현식 중 일부가 상수 표현식이 아닌 경우, 해당 후보를 제외합니다.
참고: 결과적으로, 구절 내 부분 표현식 중 하나라도 상수 표현식이 아니면, 구절 내 모든 부분 표현식은 구체 타입이어야 합니다.
-
후보 순위 결정: 두 오버로드 후보 C1, C2에 대해, C1이 더 우선됨이란 다음의 조건을 만족할 때입니다:
-
P의 각 표현식 위치 i에서 C1.R(i) ≤ C2.R(i).
-
즉, C1을 P에 적용하는 데 필요한 각 표현식 변환이 C2를 적용할 때 요구되는 표현식 변환과 최소한 동일하거나 더 우선됨.
-
-
적어도 한 표현식 위치 i에서 C1.R(i) < C2.R(i).
-
즉, C1을 적용할 때 요구되는 표현식 변환 중 적어도 하나는 C2를 적용할 때보다 엄격히 더 우선됨.
-
-
-
모든 후보에 대해 더 우선되는 단일 후보 C가 있으면 오버로드 해결이 성공하며, 해당 후보 타입 규칙 C를 반환합니다. 그렇지 않으면 오버로드 해결은 실패합니다.
6.2. 단순 타입
단순 타입이란 불리언 값, 숫자, 벡터, 행렬, 또는 이러한 값들의 집합의 기계적 표현을 위한 타입입니다.
단순 타입은 스칼라 타입, 원자적 타입, 또는 복합 타입 중 하나입니다.
참고: WGSL의 단순 타입은 C++의 Plain-Old-Data 타입과 유사하지만, 원자적 타입과 추상 수치 타입도 포함합니다.
6.2.1. 추상 수치 타입
이 타입들은 WGSL 소스에서 표기할 수 없습니다. 오직 타입 검사에만 사용됩니다.
특정 표현식은 셰이더 생성 시점에 평가되며, GPU가 직접 구현하는 것보다 더 넓은 숫자 범위와 정밀도를 가질 수 있습니다.
WGSL은 이러한 평가를 위해 두 가지 추상 수치 타입을 정의합니다:
-
AbstractInt 타입은 64비트 이진수 보수(two’s complement) 형식으로 표현 가능한 정수 집합입니다. 최상위 비트가 부호 비트입니다.
-
AbstractFloat 타입은 IEEE-754 binary64(배정밀도) 형식으로 표현 가능한 유한 부동 소수점 수 집합입니다.
이 타입들로 표현식을 평가할 때 오버플로우, 무한값 또는 NaN 값이 발생하면 안 됩니다.
타입이 추상 수치 타입이거나 추상 수치 타입을 포함하면 추상 타입입니다. 추상 타입이 아니면 구체 타입입니다.
숫자 리터럴에 접미사가 없으면 추상 수치 타입의 값을 나타냅니다:
-
정수 리터럴에
i
또는u
접미사가 없으면 AbstractInt 값을 나타냅니다. -
부동 소수점 리터럴에
f
또는h
접미사가 없으면 AbstractFloat 값을 나타냅니다.
예시: log2(32)
표현식의 분석 과정:
-
log2(32)
는log2
내장 함수에 AbstractInt 값 32를 인자로 호출한 것으로 파싱됩니다. -
log2
에 정수 스칼라 형식 매개변수를 받는 오버로드는 없습니다. -
대신 오버로드 해결이 적용되어, 세 가지 가능한 오버로드와 가능한 자동 변환을 고려합니다:
-
AbstractInt에서 AbstractFloat로. (변환 순위 4)
-
AbstractInt에서 f32로. (변환 순위 5)
-
AbstractInt에서 f16로. (변환 순위 6)
-
-
결국 연산은 AbstractFloat(예:
log2(32.0)
)에서 수행됩니다.
예시: 1 + 2.5
표현식의 분석 과정:
-
1 + 2.5
는 AbstractInt 값 1, AbstractFloat 값 2.5의 덧셈 연산으로 파싱됩니다. -
정수 타입과 부동 소수점 타입의 덧셈 오버로드는 없습니다.
-
그러나 가능한 자동 변환을 이용하면 세 가지 오버로드가 있습니다:
-
1
이 AbstractFloat 값1.0
으로 변환(순위 4),2.5
는 AbstractFloat 그대로(순위 0).
-
-
첫 번째 오버로드가 우선 후보이며 타입 검사가 성공합니다.
-
결국 연산은 AbstractFloat
1.0 + 2.5
로 수행됩니다.
예시: let x = 1 + 2.5;
-
이 예시는 위와 유사하지만,
x
는 추상 수치 타입으로 해석될 수 없습니다. -
선언의 효과는
let x : f32 = 1.0f + 2.5f;
와 같습니다.
예시: 1u + 2.5
는 셰이더 생성 오류가 발생합니다:
-
1u
는 u32 타입의 표현식입니다. -
2.5
는 AbstractFloat 타입의 표현식입니다. -
유효한 오버로드 후보가 없습니다:
예시: -1 * i32(-2147483648)
는 셰이더 생성 오류가 발생하지 않습니다:
-
-1
는 AbstractInt 타입의 표현식입니다. -
i32(-2147483648)
는 i32 타입의 표현식입니다. -
이 두 타입에 대해 곱셈 연산자 오버로드가 없고, i32는 AbstractInt로 업컨버전(up-convert)할 수 없습니다.
-
가능한 자동 변환은 AbstractInt를 i32로 변환하는 것 뿐이므로:
// 명시적 타입의 부호 없는 정수 리터럴 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 타입은 true
와
false
값을 포함합니다.
전제 조건 | 결론 | 설명 |
---|---|---|
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를 벡터의 성분 타입이라고 합니다. |
벡터의 성분 타입이 수치 스칼라인 경우 수치 벡터입니다.
벡터의 주요 용도는 다음과 같습니다:
-
방향과 크기를 동시에 표현
-
공간상의 위치 표현
-
어떤 색 공간 내의 색상 표현 예를 들어, 성분이 빨강, 초록, 파랑의 강도일 수 있고, 네 번째 성분은 알파(불투명도) 값일 수 있습니다.
벡터(및 행렬)에 대한 많은 연산은 성분별로 동작하며, 즉 결과는 각 스칼라 성분에 대해 독립적으로 연산하여 만들어집니다.
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]
사전 선언된 별칭 | 원래 타입 | 제한 사항 |
---|---|---|
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}에 속해야 하며, T는 f32, f16, 또는 AbstractFloat이어야 합니다. 동치적으로, C개의 vecR<T> 타입의 열벡터로 볼 수 있습니다. |
행렬의 주요 용도는 선형 변환을 구현하는 것입니다. 이 해석에서 행렬의 벡터들은 열벡터로 취급됩니다.
곱셈 연산자(*
)는 다음에 사용됩니다:
-
변환을 스칼라 크기로 스케일링
-
벡터에 변환 적용
-
다른 행렬과 변환 결합
§ 8.7 산술 표현식 참고.
사전 선언된 별칭 | 원래 타입 | 제한 사항 |
---|---|---|
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은 다음 제약을 만족해야 합니다:
-
N이 0 이하인 경우:
-
그 외에는 파이프라인 생성 오류입니다.
참고: 원소 수 값은 N이 override 선언에 의존하면 파이프라인 생성 시점에, 그렇지 않으면 셰이더 모듈 생성 시점에 완전히 결정됩니다.
참고: 타입 동치성(type-equivalency)을 만족하려면, 상수 표현식이 아닌 override 표현식은 반드시 식별자여야 합니다. Override 가능한 상수로 크기가 지정된 workgroup 변수 예시 참고
런타임 크기 배열의 원소 수는 해당 storage buffer 변수에 연결된 버퍼 바인딩의 크기로 결정됩니다. § 13.3.4 버퍼 바인딩에 의해 결정되는 런타임 크기 배열 원소 수 참고.
배열의 원소 타입은 다음 중 하나여야 합니다:
-
스칼라 타입
-
벡터 타입
-
행렬 타입
-
원자적 타입
-
배열 타입(단, 생성 시점 고정 footprint를 가져야 함)
-
구조체 타입(단, 생성 시점 고정 footprint를 가져야 함)
참고: 원소 타입은 단순 타입이어야 합니다.
두 배열 타입이 동일한 경우는 다음 모두를 만족할 때입니다:
-
원소 타입이 동일
-
원소 수 규격이 일치, 즉 다음 중 하나:
-
둘 다 런타임 크기
-
둘 다 생성 시점 고정 footprint의 고정 크기이고, 원소 수가 동일한 값(부호가 다르더라도). (이 경우 부호가 다르더라도 원소 수는 항상 양수이므로 비교 가능)
-
둘 다 식별자로 지정된 고정 크기이며, 해당 식별자가 같은 파이프라인 오버라이드 가능 상수 선언으로 해석됨.
-
// 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 변수와 값 선언 참고.
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 선언과 범위 참고.
두 구조체 타입이 동일하려면 이름이 같아야 합니다.
구조체 멤버 타입은 다음 중 하나여야 합니다:
-
스칼라 타입
-
벡터 타입
-
행렬 타입
-
원자적 타입
-
고정 크기 배열 타입(단, 생성 시점 고정 footprint를 가져야 함)
-
런타임 크기 배열 타입(단, 구조체의 마지막 멤버여야 함)
-
구조체 타입(단, 생성 시점 고정 footprint를 가져야 함)
참고: 모든 사용자 선언 구조체 타입은 구체 타입입니다.
참고: 각 멤버 타입은 단순 타입이어야 합니다.
구조체 멤버, 배열 원소 타입의 제한에 따른 결과:
-
포인터, 텍스처, 샘플러는 어떠한 배열/구조체 중첩에서도 나타나면 안 됩니다.
-
런타임 크기 배열이 더 큰 타입의 일부일 경우, 반드시 구조체의 마지막 원소로만 나타나야 하며, 해당 구조체는 다른 배열/구조체에 포함될 수 없습니다.
// 세 개 멤버를 가진 구조체 struct Data { a : i32, b : vec2< f32> , c : array< i32, 10 > , // 마지막 컴마는 선택사항 } // Data 타입 값을 저장하는 변수 선언 var < private> some_data : Data ;
'struct'
ident struct_body_decl
'{'
struct_member ( ','
struct_member ) * ','
? '}'
구조체 멤버에 적용할 수 있는 속성(attribute)은 다음과 같습니다:
builtin, location, blend_src, interpolate, invariant 속성은 IO 속성입니다. 구조체 S의 멤버에 적용된 IO 속성은 S가 형식 매개변수 또는 반환 타입으로서 엔트리 포인트에서 사용될 때에만 효과가 있습니다. § 13.3.1 셰이더 단계 간 입력/출력 인터페이스 참고.
align, size 속성은 레이아웃 속성이며, 구조체 타입이 유니폼 버퍼 또는 스토리지 버퍼 정의에 사용될 때 필요할 수 있습니다. § 14.4 메모리 레이아웃 참고.
// 런타임 배열 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)는 다음과 같습니다:
-
벡터 타입이면 1
-
행렬 타입이면 2
-
배열 타입(원소 타입 E)이면 1 + NestDepth(E)
-
구조체 타입(멤버 타입 M1,...,MN)이면 1 + max(NestDepth(M1),..., NestDepth(MN))
6.2.12. 생성 가능한 타입
많은 종류의 값은 생성, 로드, 저장, 함수에 전달, 함수에서 반환할 수 있습니다. 이를 생성 가능이라고 합니다.
다음 중 하나인 타입은 생성 가능 타입입니다:
-
스칼라 타입
-
벡터 타입
-
행렬 타입
-
고정 크기 배열 타입(단, 생성 시점 고정 footprint를 가지며 원소 타입도 생성 가능이어야 함)
-
구조체 타입(단, 모든 멤버가 생성 가능이어야 함)
참고: 모든 생성 가능 타입은 생성 시점 고정 footprint를 가집니다.
참고: 원자적 타입과 런타임 크기 배열 타입은 생성 가능이 아닙니다. 원자적 또는 런타임 크기 배열을 포함하는 복합 타입도 생성 가능이 아닙니다.
6.2.13. 고정 footprint 타입
변수의 메모리 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
참고: | 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 선언에 설명된 대로, 유니폼 버퍼와 스토리지 버퍼 변수의 스토어 타입은 반드시 호스트 공유 가능 타입이어야 합니다.
타입이 호스트 공유 가능이려면, 구체 타입이면서 다음 중 하나여야 합니다:
-
수치 스칼라 타입
-
수치 벡터 타입
-
행렬 타입
-
원자적 타입
-
고정 크기 배열 타입(단, 생성 시점 고정 footprint를 가지며 원소 타입도 호스트 공유 가능이어야 함)
-
런타임 크기 배열 타입(단, 원소 타입도 호스트 공유 가능이어야 함)
-
구조체 타입(단, 모든 멤버가 호스트 공유 가능이어야 함)
참고: 셰이더 단계 간 입력/출력 타입의 제한은 § 13.3.1 셰이더 단계 간 입력/출력 인터페이스 및 이후 절에 설명되어 있습니다. 해당 타입들도 사이즈가 결정되지만, 계산 방식이 다릅니다.
참고: 텍스처와 샘플러도 호스트와 GPU 사이에서 공유될 수 있지만, 내용은 불투명합니다. 이 절의 호스트 공유 가능 타입은 특히 storage와 uniform 버퍼에 사용됩니다.
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 소스 텍스트에서는:
-
참조 타입은 나타나면 안 됩니다.
-
포인터 타입은 나타날 수 있습니다.
-
포인터 타입은 다음을 파라미터화하여 표기됩니다:
-
때때로 접근 모드도, § 14.3 주소 공간에 명시된 대로.
-
프로그램 소스에 포인터 타입이 나타나면, 해당 포인터 타입의 주소 공간, 스토어 타입, 접근 모드로 변수를 선언하는 것도 반드시 가능해야 합니다.
참고: 이 제한은 런타임에서 사용할 수 없는 타입 별칭과 함수 형식 매개변수 선언을 금지합니다. 이 제한이 없으면 포인터 타입의 별칭 선언은 유효하지만 해당 타입의 포인터 값을 생성할 수 없게 됩니다. 마찬가지로 포인터 형식 매개변수를 가진 함수 선언은 가능하지만 해당 함수를 호출할 수 없게 됩니다.
-
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에 대응하며, 그 반대도 성립합니다. 여기서 p와 r는 동일한 메모리 뷰를 나타냅니다.
6.4.4. 유효/무효 메모리 참조
참조는 § 6.4.8 참조와 포인터 값 형성에서 자세히 설명된 대로 형성됩니다. 일반적으로 유효 참조는 다음과 같이 형성됩니다:
-
변수 이름으로, 또는
-
메모리 뷰가 유효한 base인 이름 있는 성분 표현식, 또는
일반적으로 무효 메모리 참조는 다음과 같이 형성됩니다:
-
무효 포인터에 간접 연산자를 적용, 또는
-
base가 무효 메모리 참조인 이름 있는 성분 표현식, 또는
유효 포인터는 유효 참조에 대응하는 포인터입니다. 무효 포인터는 무효 메모리 참조에 대응하는 포인터입니다.
6.4.5. 기원 변수
-
R이 변수인 경우, 해당 변수.
-
R이 P 포인터에 단항 * 연산을 적용한 경우, P의 기원 변수.
-
R이 base의 이름 있는 성분 표현식 또는 인덱싱 표현식이라면, base의 기원 변수.
포인터 값의 기원 변수는 해당 참조 값의 기원 변수로 정의됩니다.
참고: 기원 변수는 동적 개념입니다. 함수의 형식 매개변수에 대한 기원 변수는 호출 지점에 따라 다릅니다. 서로 다른 호출 지점에서 서로 다른 기원 변수에 대한 포인터를 전달할 수 있습니다.
유효 참조는 항상 어떤 변수의 메모리 위치의 일부 또는 전체에 대해 비어 있지 않은 메모리 뷰에 대응합니다.
아래 예시에서 the_particle.position[i]
참조는 i
가 0 또는 1일 때만 유효합니다.
i
가 2일 때는 이 참조는 무효 메모리 참조가 되며, 실제로는
the_particle.color_index
의 메모리 위치에 대응합니다.
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. 참조 및 포인터 활용 사례
참조와 포인터는 사용 방식에 따라 구분됩니다:
-
변수의 타입은 참조 타입입니다.
-
주소 연산(단항
&
)은 참조 값을 그에 대응하는 포인터 값으로 변환합니다. -
간접 참조(단항
*
)는 포인터 값을 그에 대응하는 참조 값으로 변환합니다. -
let 선언은 포인터 타입일 수 있지만, 참조 타입일 수는 없습니다.
-
형식 매개변수는 포인터 타입일 수 있지만, 참조 타입일 수는 없습니다.
-
로드 규칙: 함수 내부에서 참조는 타입 규칙을 만족시키기 위해 자동으로 역참조(읽기)됩니다:
-
함수에서 스토어 타입 T의 참조 표현식 r이 문 또는 표현식에서 사용될 때,
-
r의 접근 모드가 read 또는 read_write이고,
-
잠재적으로 일치하는 타입 규칙이 r이 타입 T 값을 가져야 하는 경우에만 있다면,
-
해당 타입 규칙 요구 사항이 만족된 것으로 간주되고,
-
그 문맥에서 r를 평가한 결과는 r가 참조하는 메모리 위치에서 평가 시점에 저장된 T 타입 값입니다. 즉, 결과 값을 얻기 위해 읽기 접근이 수행됩니다.
-
이렇게 참조를 정의하면 변수의 간단한 관용적 사용이 가능합니다:
@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 (); }
이렇게 포인터를 정의하면 두 가지 주요 활용 사례가 가능합니다:
-
포인터 타입의 let 선언을 사용하여 변수 내용의 일부에 대해 짧은 이름을 만듦.
-
함수의 형식 매개변수로 포인터를 사용하여 호출 함수가 접근할 수 있는 변수의 메모리에 직접 접근함.
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. 참조와 포인터 값 형성
참조 값은 다음 방법 중 하나로 형성됩니다:
-
포인터에 간접 참조(단항
*
) 연산을 사용. -
메모리 뷰를 복합 타입에 대한 이름 있는 성분 표현식으로 사용:
-
스토어 타입이 벡터인 메모리 뷰의 경우, 한 글자 벡터 접근 구문을 붙이면 벡터의 해당 성분에 대한 참조가 된다. § 8.5.1.3 벡터 메모리 뷰의 성분 참조 참고.
-
스토어 타입이 구조체인 메모리 뷰의 경우, 멤버 접근 구문을 붙이면 구조체의 해당 멤버에 대한 참조가 된다. § 8.5.4 구조체 접근 표현식 참고.
-
-
메모리 뷰를 복합 타입에 대한 인덱싱 표현식으로 사용:
-
스토어 타입이 벡터인 메모리 뷰의 경우, 배열 인덱스 접근 구문을 붙이면 벡터의 해당 인덱스 성분에 대한 참조가 된다. § 8.5.1.3 벡터 메모리 뷰의 성분 참조 참고.
-
스토어 타입이 행렬인 메모리 뷰의 경우, 배열 인덱스 접근 구문을 붙이면 행렬의 해당 열 벡터에 대한 참조가 된다. § 8.5.2 행렬 접근 표현식 참고.
-
스토어 타입이 배열인 메모리 뷰의 경우, 배열 인덱스 접근 구문을 붙이면 배열의 해당 원소에 대한 참조가 된다. § 8.5.3 배열 접근 표현식 참고.
-
모든 경우에서 결과의 접근 모드는 원래 참조의 접근 모드와 동일합니다.
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의 참조와 포인터는 다른 언어보다 더 제한적입니다. 특히:
-
WGSL에서는 참조를 다른 참조나 변수의 별칭(alias)로 직접 선언할 수 없습니다. 변수나 함수 형식 매개변수로서도 불가능합니다.
-
WGSL에서 포인터와 참조는 저장 가능이 아닙니다. 즉, WGSL 변수 선언의 내용으로 포인터나 참조를 포함할 수 없습니다.
-
WGSL에서는 함수가 포인터 또는 참조를 반환하면 안 됩니다.
-
WGSL에서는 정수 값과 포인터 값 사이의 변환 방법이 없습니다.
-
WGSL에서는 포인터 값을 강제로 다른 포인터 타입으로 바꾸는 방법이 없습니다.
-
복합 성분 참조 표현식은 다릅니다: 복합 값에 대한 참조를 받아 그 값 내부 성분이나 원소에 대한 참조를 반환합니다. WGSL에서는 두 참조가 다르다고 간주되며, 구현의 더 낮은 수준에서 머신 주소가 같더라도 구분됩니다.
-
-
WGSL에서는 참조 값을 강제로 다른 참조 타입으로 바꾸는 방법이 없습니다.
-
WGSL에서는 포인터나 참조의 접근 모드를 변경하는 방법이 없습니다.
-
비교하면, C++은 비-const 포인터를 const 포인터로 자동 변환하고,
const_cast
를 통해 const 값을 비-const 값으로 변환할 수 있습니다.
-
-
WGSL에서는 "힙"에서 새로운 메모리를 할당하는 방법이 없습니다.
-
WGSL에서는 변수를 명시적으로 파괴(destroy)할 방법이 없습니다. WGSL 변수의 메모리는 변수가 스코프를 벗어날 때만 접근할 수 없게 됩니다.
참고: 위 규칙에 따라 "댕글링(dangling)" 포인터, 즉 "live" 기원 변수를 참조하지 않는 포인터를 만드는 것은 불가능합니다. 메모리 뷰는 무효 메모리 참조일 수 있지만, 절대 기원 변수 또는 버퍼와 연결되지 않은 메모리 위치를 접근하지 않습니다.
6.5. 텍스처 및 샘플러 타입
텍셀(texel)은 텍스처의 가장 독립적으로 접근 가능한 최소 단위 성분인 스칼라 또는 벡터입니다. texel은 texture 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)
-
텍스처가 멀티샘플일 때 샘플 개수.
텍스처의 각 텍셀은 고유한 논리적 텍셀 주소와 연결되어 있습니다. 논리적 텍셀 주소는 다음을 포함하는 정수 튜플입니다:
-
[0, mip level count) 범위의 mip level
텍스처의 물리적 조직은 렌더링 연산에 최적화되어 있습니다. 이를 위해 데이터 레이아웃, 데이터 타입, 셰이더 언어에서 직접 표현할 수 없는 내부 연산 등 많은 세부사항이 프로그래머에게 숨겨집니다.
결과적으로 셰이더는 텍스처 변수 내의 텍셀 메모리에 직접 접근할 수 없습니다. 대신 불투명 핸들을 통해 접근합니다:
-
셰이더 내부에서:
-
WebGPU 파이프라인 구성 시, 텍스처 변수의 스토어 타입과 바인딩은 해당 바인드 그룹 레이아웃 항목과 호환되어야 합니다.
이러한 방식으로, 텍스처 타입의 지원 연산 집합은 해당 텍스처 타입을 가진 텍스처 내장 함수의 존재 여부에 따라 결정됩니다. 즉, 해당 타입이 형식 매개변수인 내장 함수가 있어야 합니다.
참고: 텍스처 변수에 저장된 핸들은 셰이더에서 변경될 수 없습니다. 즉, 변수는 읽기 전용이며, 실제로 접근하는 하부 텍스처가 변경 가능(예: 쓰기 전용 스토리지 텍스처)일 수 있더라도 그렇습니다.
텍스처 타입은 다음에 정의된 타입 집합입니다:
샘플러는 텍셀을 샘플드 텍스처 또는 깊이 텍스처에서 어떻게 접근할지 제어하는 불투명 핸들입니다.
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개의 채널을 모두 갖지 않을 때는:
-
텍셀을 읽을 때 채널 변환 함수가 성분별로 적용됩니다:
-
녹색 채널이 없으면 셰이더 값의 두 번째 성분은 0입니다.
-
파랑 채널이 없으면 셰이더 값의 세 번째 성분은 0입니다.
-
알파 채널이 없으면 셰이더 값의 네 번째 성분은 1입니다.
-
아래 표의 마지막 열은 채널 포맷 표의 포맷별 채널 변환 함수를 사용합니다.
텍셀 포맷 | 채널 포맷 | 메모리 순서의 채널들 | 해당 셰이더 값 |
---|---|---|---|
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
| 맞음 |
-
T는 샘플드 타입입니다.
-
이미지의 매개변수 타입은 샘플링 후 변환된 타입입니다. 예를 들어, 텍셀이 8비트 unorm 성분을 가지는 이미지를 샘플링하면 32비트 float 결과(vec-of-f32)로 얻을 수 있습니다.
6.5.3. 멀티샘플 텍스처 타입
멀티샘플 텍스처는 샘플 개수가 1 이상입니다. 이름과 달리 샘플러와 함께 사용할 수 없습니다. 실질적으로 샘플 인덱스를 무시하면 논리적 텍셀 주소당 여러 텍셀 데이터를 저장합니다.
텍셀 포맷은 텍스처 변수에 바인딩된
format
속성입니다.
WebGPU는 텍스처, 바인드 그룹 레이아웃의 sampleType
,
그리고 텍스처 변수의 샘플드 타입간의 호환성을 검증합니다.
texture_multisampled_2d
는 샘플드 타입으로 매개변수화되며,
반드시
f32
, i32
, u32
중 하나여야 합니다.
타입 | 차원 | 배열 여부 |
---|---|---|
texture_multisampled_2d<T> | 2D
| 아님 |
texture_depth_multisampled_2d | 2D
| 아님 |
-
T는 샘플드 타입입니다.
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
| 아님 |
-
Format은 스토리지 텍스처용 텍셀 포맷의 열거자여야 합니다
-
Format과 Access의 잘못된 조합으로 인해 셰이더 생성 오류가 발생하지 않습니다. 조합은 파이프라인 생성 중 셰이더 바인딩 검증 단계에서 확인되며, 잘못된 조합은 파이프라인 생성 오류를 일으킵니다.
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 멤버에도 적용됩니다.
'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 타입 표현식 참고.
참고: 표현식도 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 ;
각 선언은 반드시 명시적으로 타입이나 초기화자를 지정해야 합니다. 타입과 초기화자를 모두 지정할 수 있습니다. 각 선언은 연관된 데이터 값의 타입을 결정하는데, 이를 해당 선언의 실효 값 타입이라고 합니다. 실효 값 타입은 다음과 같습니다:
-
명시적으로 지정한 타입이 있으면 그 타입
-
그렇지 않고, 초기화자 표현식의 타입이
T
라면:-
const
선언의 경우, 실효 값 타입은T
자체입니다. -
override
,let
,var
선언의 경우, 실효 값 타입은T
의 구체화입니다.
-
각 값 또는 변수 선언 종류는, 초기화자 표현식의 형태 및 실효 값 타입에 대해 추가 제약을 둘 수 있습니다.
선언 | 변경 가능성 | 스코프 | 실효 값 타입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 표현식, 또는 런타임 표현식 | 아님 |
-
초기화자가 지정되지 않은 경우, 파이프라인 생성 시점에 값이 제공되어야 합니다.
-
Override 선언은 셰이더 인터페이스의 일부이지만, 바인드 리소스는 아닙니다.
-
스토리지 버퍼 및 스토리지 텍스처 중 read 이외의 접근 모드를 가진 것은 vertex 셰이더 단계에서 정적으로 접근될 수 없습니다. WebGPU
createBindGroupLayout()
참고. -
원자적 타입은 변경 가능한 스토리지 버퍼 또는 workgroup 변수에만 나타날 수 있습니다.
-
스토리지 텍스처 중 write 또는 read_write 접근 모드의 데이터는 변경 가능하지만, textureStore 내장 함수를 통해서만 수정할 수 있습니다. 변수 자체는 수정할 수 없습니다.
-
workgroup 주소 공간의 변수는 compute 셰이더 단계에서만 정적으로 접근될 수 있습니다.
-
가장 바깥쪽 배열의 원소 수는 override 표현식일 수 있습니다.
-
초기화자가 없으면 변수는 기본값으로 초기화됩니다.
7.1. 변수 vs 값
변수 선언은 WGSL 모듈에서 유일하게 변경 가능한 데이터입니다. 값 선언은 항상 불변입니다. 변수는 참조 및 포인터 값의 기반이 될 수 있는데, 변수는 메모리 위치와 연관되어 있기 때문입니다. 반면 값 선언은 포인터나 참조 값의 기반이 될 수 없습니다.
변수 사용은 값 선언 사용보다 일반적으로 비용이 더 높습니다. 변수 사용 시 읽기 또는 쓰기를 위해 해당 변수의 메모리 위치를 추가로 접근해야 하기 때문입니다.
일반적으로 작성자는 다음 순서대로 선언을 사용하는 것이 좋으며, 첫 번째가 가장 추천되는 옵션입니다:
이 순서대로 사용하면 셰이더의 전체 성능이 가장 좋습니다.
7.2. 값 선언
식별자가 값 선언으로 해석되면, 해당 식별자는 그 값을 나타냅니다.
WGSL은 여러 종류의 값 선언을 제공합니다. 각 선언 종류별 값은 셰이더 생명주기의 서로 다른 시점에 고정됩니다. 각 값 선언 종류와 값이 고정되는 시점은 다음과 같습니다:
-
let 선언: 실행 시점
7.2.1. const
선언
const 선언은 셰이더 생성 시점에 고정되는 데이터 값에 이름을 지정합니다. 각 const 선언에는 반드시 초기화자가 필요합니다. const 선언은 모듈 또는 함수 범위에서 선언할 수 있습니다. 초기화자 표현식은 상수 표현식이어야 합니다. const 선언의 타입은 구체 또는 추상 생성 가능 타입이어야 합니다. const 선언만 실효 값 타입이 추상일 수 있습니다.
참고: 추상 숫자 타입은 WGSL에서 표기할 수 없으므로, 타입 추론을 통해서만 사용할 수 있습니다.
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 선언의 실효 값 타입은 구체 생성 가능 타입 또는 포인터 타입이어야 합니다.
// '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>
입니다.
변수의 스토어 타입은 항상 구체 타입입니다.
변수 선언은 다음을 지정합니다:
-
변수의 이름
-
변수의 주소 공간, 스토어 타입, 접근 모드를 결정합니다. 이 세 가지가 함께 변수의 참조 타입을 이룹니다.
-
스토어 타입은 변수 선언의 실효 값 타입입니다.
-
-
실행 환경이 변수의 수명 동안, 지정된 주소 공간에 지정된 접근 모드로 스토어 타입 값의 메모리를 할당하도록 보장합니다.
-
변수가 private 또는 function 주소 공간에 있을 때는 선택적으로 초기화자 표현식을 가질 수 있습니다. 있으면, 초기화자는 변수의 스토어 타입으로 평가되어야 합니다. private 변수의 초기화자는 반드시 상수 표현식 또는 override 표현식이어야 합니다. function이나 private 외의 주소 공간에 있는 변수는 초기화자를 가져서는 안 됩니다.
식별자가 변수 선언으로 해석되면, 해당 식별자는 변수 메모리에 대한 참조 메모리 뷰를 나타내는 표현식이며, 타입은 변수의 참조 타입입니다. § 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 리소스 인터페이스에 설명된 대로, 유니폼 버퍼, 스토리지 버퍼, 텍스처, 샘플러는 셰이더의 리소스 인터페이스를 이룹니다.
변수의 수명이란 셰이더 실행 중, 해당 변수에 메모리 위치가 연관되는 기간입니다. 모듈 범위 변수의 수명은 셰이더 스테이지 전체 실행 기간입니다. private와 function 주소 공간에는 각 호출별로 독립적인 변수가 존재합니다. 함수 범위 변수는 동적 컨텍스트입니다. 함수 범위 변수의 수명은 스코프에 의해 결정됩니다:
-
변수 선언에 진입할 때 시작합니다.
-
이름이 해당 동적 컨텍스트의 어느 부분에서도 더 이상 스코프 내에 있지 않을 때 끝납니다. 즉, 수명에는 이름이 스코프 내에 있을 때 호출된 함수도 포함됩니다.
두 리소스 변수는 중첩된 메모리 위치를 가질 수 있지만, 둘 중 하나라도 변경 가능하면 동적 오류입니다. 수명이 중첩되는 다른 변수들은 절대 중첩된 메모리 위치를 가지지 않습니다. 변수의 수명이 끝나면 해당 메모리는 다른 변수에 사용될 수 있습니다.
참고: WGSL은 변수의 내용이 오직 변수의 수명 동안만 관찰 가능함을 보장합니다.
private, function, workgroup 주소 공간 변수는 생성 시 반드시 초기값을 가집니다. 초기화자가 없으면 초기값은 기본 초기값입니다. 초기값 계산 방식은 아래와 같습니다:
-
function 주소 공간 변수의 경우:
-
private 주소 공간 변수의 경우:
-
그 외에는, override 표현식으로서 구체화 평가 결과입니다. 초기화자는 파이프라인 생성 시점보다 늦게 고정될 수 없습니다.
-
workgroup 주소 공간 변수의 경우:
그 외 주소 공간의 변수는 리소스로서 draw 명령 또는 dispatch 명령의 바인딩으로 설정됩니다.
다음 WGSL 코드 스니펫을 참고하세요:
var i : i32; // 초기값은 0. 권장 스타일 아님. loop { var twice : i32= 2 * i ; // 반복마다 재평가됨. i ++ ; if i == 5 { break ; } }
i
는 0, 1, 2, 3, 4, 5 값을 갖게 되며, 변수 twice
는 0, 2, 4, 6, 8 값을 갖게 됩니다.
다음 WGSL 코드 스니펫을 참고하세요:
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_decl '='
expression
| 'let'
optionally_typed_ident '='
expression
| 'const'
optionally_typed_ident '='
expression
'var'
_disambiguate_template template_list ? optionally_typed_ident
ident ( ':'
type_specifier ) ?
attribute * variable_decl ( '='
expression ) ?
'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는 다음 경우에만 평가됩니다:
-
E가 최상위 표현식인 경우
-
E가 표현식 OuterE의 하위 표현식이고, OuterE가 평가되어야 하며, OuterE의 평가에 E의 평가가 필요한 경우
-
E가 표현식 OuterE의 하위 표현식이고, OuterE의 정적 타입을 결정하기 위해 E의 평가가 필요한 경우
-
E가 표현식 OuterE의 하위 표현식이고, OuterE가 E를 평가해야 셰이더 생성 오류를 발생시키는 경우 (예: 정수 나눗셈)
참고: 이 평가 규칙에 따라 단락 연산자 &&
와
||
는
오른쪽 하위 표현식의 평가를 보호하며, 하위
표현식의 평가가 정적 타입 결정을 위해 필요하지
않는 한 평가되지 않습니다.
상수 표현식은 WebGPU API 메서드를 구현하는 CPU에 의해 평가될 수 있습니다. 따라서 AbstractFloat 값 연산의 정확도 요구사항은 WebAssembly [WASM-CORE-2]와 ECMAScript [ECMASCRIPT] 등 일반 WebGPU 런타임 환경에서 요구하는 것보다 더 엄격하지 않습니다. 구체 부동소수점 타입(f32 등) 연산의 정확도 요구사항은 § 15.7.4.1 구체 부동소수점 표현식의 정확도에서 명시합니다.
예시: (42)
의 분석은 다음과 같습니다:
-
42
는 AbstractInt 값 42입니다. -
괄호로 감싸면
(42)
라는 새로운 표현식이 만들어지고, 타입은 값이 42인 AbstractInt가 됩니다.
예시: -5
의 분석은 다음과 같습니다:
-
5
는 AbstractInt 값 5입니다. -
그 앞에 '
-
'를 붙이면-5
라는 새로운 표현식이 만들어지고, 타입은 값이 -5인 AbstractInt가 됩니다.
예시: -2147483648
의 분석은 다음과 같습니다:
-
2147483648
는 AbstractInt 값 2147483648입니다. 이 값은 32비트 부호 있는 정수에는 들어가지 않습니다. -
그 앞에 '
-
'를 붙이면-2147483648
라는 새로운 표현식이 만들어지고, 타입은 값이 -2147483648인 AbstractInt가 됩니다.
예시: const minint = -2147483648;
의 분석은 다음과 같습니다:
-
위와 같이
-2147483648
은 AbstractInt 값 -2147483648로 평가됩니다. -
이로 인해
minint
는 -2147483648인 AbstractInt 값을 갖는 것으로 선언됩니다.
예시: let minint = -2147483648;
의 분석은 다음과 같습니다:
-
위와 같이
-2147483648
은 AbstractInt 값 -2147483648로 평가됩니다. -
let 선언에 명시적 타입이 없으므로 오버로드 해석이 사용됩니다. 적용 가능한 오버로드 후보들은 가능한 자동 변환을 통해 AbstractInt에서 i32, u32, f32로 변환합니다. 가장 우선순위가 낮은 것은 i32이며, -2147483648인 AbstractInt 값이 i32 값 -2147483648로 변환됩니다.
-
결과적으로
minint
는 -2147483648인 i32 값으로 선언됩니다.
예시: false && (10i < i32(5 * 1000 * 1000 * 1000))
의 분석은 다음과 같습니다:
-
전체 표현식은 상수 표현식입니다.
-
하지만
&&
연산자의 단락 규칙에 따라 좌변이false
로 평가되면 우변은 평가되지 않습니다. -
만약 i32(5 * 1000 * 1000 * 1000)를 평가했다면, AbstractInt 값 5000000000이 i32 타입을 오버플로우시켜 셰이더 생성 오류가 발생했을 것입니다.
예시: false && array<u32, 1 + 2>(0, 1, 2)[0] == 0
-
전체 표현식은 상수 표현식입니다.
-
타입 검사는
e1 : bool && e2 : bool
을 요구합니다:-
false
는 bool 값입니다. -
타입 검사는 우변을 진행하며, 결국 배열 원소 개수 표현식에서
1 + 2
를 평가합니다.
-
-
1 + 2
는 i32 값3
으로 평가됩니다.-
배열 타입은
array<u32, 3i>
가 됩니다.
-
-
배열 접근 표현식과 동등 연산자는 평가되지 않습니다.
8.1.2. override
표현식
파이프라인 생성 시점에 평가될 수 있는 표현식을 override 표현식이라고 합니다. 어떤 표현식이 override 표현식이 되려면 모든 식별자가 다음 중 하나로 해석되어야 합니다:
참고: 모든 상수 표현식은 override 표현식이기도 합니다.
상수 표현식이 아닌 override 표현식은 파이프라인 생성 중에만 검증 또는 평가되며, API로 제공된 값이 override 선언에 대체된 후에만 평가됩니다. 만약 override 선언의 값이 API를 통해 대체되면, 초기화자 표현식(있을 경우)은 평가되지 않습니다. 그렇지 않으면, override 표현식 E는 다음 경우에만 평가됩니다:
-
E가
GPUProgrammableStage
로 지정된 엔트리 포인트 셰이더의 일부이고, -
다음 중 하나가 참일 때:
참고: 모든 override 표현식이 override 선언의 초기화자로 사용될 수 있는 것은 아닙니다. 이러한 초기화자는 반드시 타입 규칙에 따라 구체 스칼라 타입으로 해석되어야 합니다.
예시: override x = 42;
의 분석은 다음과 같습니다:
-
42
는 AbstractInt 값 42입니다. -
override 선언은 구체 스칼라 타입이어야 합니다.
예시: let y = x + 1;
의 분석은 다음과 같습니다:
-
위에서
x
는 i32 타입입니다. -
표현식
x + 1
은 override 선언과 정수 리터럴로 구성된 override 표현식입니다. -
표현식의 타입은 i32이며, 파이프라인 생성 시점에 평가됩니다. 값은
x
가 파이프라인 생성 시점에 오버라이드되는지에 따라 달라집니다.
예시: vec3(x,x,x)
의 분석은 다음과 같습니다:
-
위에서
x
는 override 선언이고 타입은 i32입니다. -
vec3(x,x,x)
는 오직 override 선언으로만 구성된 override 표현식입니다.
override a : i32= 0 ; override b = a / 0 ; // 셰이더 생성 오류, // c를 오버라이드하려고 해도 상관없이 발생함
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 | 추상 부동소수점 리터럴 값 |
e가 i 접미사가 있는 정수 리터럴일 때
| e: i32 | 32비트 부호 있는 정수 리터럴 값 |
e가 u 접미사가 있는 정수 리터럴일 때
| e: u32 | 32비트 부호 없는 정수 리터럴 값 |
e가 f 접미사가 있는 부동소수점 리터럴일 때
| e: f32 | 32비트 부동소수점 리터럴 값 |
e가 h 접미사가 있는 부동소수점 리터럴일 때
| e: f16 | 16비트 부동소수점 리터럴 값 |
8.4. 괄호 표현식
전제조건 | 결론 | 설명 |
---|---|---|
e : T | ( e ) : T
| e로 평가됨. 괄호를 사용해 표현식을 주변 텍스트에서 분리함. |
8.5. 복합 값 분해 표현식
이 절에서는 복합 값의 성분을 얻는 표현식과, 복합 값을 담고 있는 메모리 뷰에서 성분의 참조를 얻는 방법을 설명합니다. 여기서 복합 값 또는 복합 값에 대한 메모리 뷰를 base라고 합니다.
방법은 두 가지가 있습니다:
- 이름 성분 표현식
-
base B에 대해, B 뒤에 마침표
'.'
(U+002D), 그리고 성분 이름을 적습니다. - 인덱싱 표현식
-
base 표현식 뒤에
'['
(U+005B), 인덱스 표현식, 그리고']'
(U+005D)를 붙입니다.
문법적으로, 이 두 형식은 component_or_swizzle_specifier 문법 규칙으로 구현됩니다.
-
base가 벡터 또는 벡터의 메모리 뷰일 때, N은 벡터 타입의 성분 개수입니다.
-
base가 행렬 또는 행렬의 메모리 뷰일 때, N은 행렬 타입의 열 개수입니다.
-
base가 고정 크기 배열 또는 고정 크기 배열의 메모리 뷰일 때, N은 요소 개수입니다.
인덱스 값이 범위 내 인덱스가 아니면 범위 외 인덱스입니다. 범위 외 인덱스는 흔히 프로그래밍 오류이며, 오류를 일으킵니다. 자세한 내용은 아래에서 설명합니다.
또한 벡터 타입은 다른 벡터의 성분으로 새 벡터 값을 만드는 swizzling 문법을 지원합니다.
8.5.1. 벡터 성분 접근 표현식
벡터의 성분 접근은 다음 방식으로 할 수 있습니다:
-
배열 인덱싱(
v[2]
등)을 사용 -
스위즐 이름을 사용. 문맥 의존 이름으로 성분의 편의 이름을 연속적으로 써서, 각각 소스 벡터의 성분에 대응됨.
-
색상 이름 집합:
r
,g
,b
,a
는 각각 벡터 성분 0, 1, 2, 3에 해당. -
차원 이름 집합:
x
,y
,z
,w
는 각각 벡터 성분 0, 1, 2, 3에 해당.
-
편의 이름은 .
표기법으로 접근합니다(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 : Te .r : T
|
e의 첫 번째 성분 선택
단일 문자 스위즐입니다. |
e: vecN<T> |
e.y : Te .g : T
|
e의 두 번째 성분 선택
단일 문자 스위즐입니다. |
e: vecN<T> N is 3 or 4 |
e.z : Te .b : T
|
e의 세 번째 성분 선택
단일 문자 스위즐입니다. |
e: vec4<T> |
e.w : Te .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>> I는 x , y , z , w 중 하나J는 x , y , z , w 중 하나AM은 read 또는 read_write |
e. IJ: vec2<T> | 첫 번째 성분 e.I, 두 번째 성분 e.J로 2성분 벡터 생성z 는 N이 3 또는 4일 때만 유효w 는 N이 4일 때만 유효e가 포인터면, 먼저 간접 참조를 적용하고 로드 규칙을 호출함. |
e: vecN<T> 또는
ptr<AS,vecN<T,AM>> I는 r , g , b , a 중 하나J는 r , g , b , a 중 하나AM은 read 또는 read_write |
e. IJ: vec2<T> | 첫 번째 성분 e.I, 두 번째 성분 e.J로 2성분 벡터 생성b 는 N이 3 또는 4일 때만 유효a 는 N이 4일 때만 유효e가 포인터면, 먼저 간접 참조를 적용하고 로드 규칙을 호출함. |
e: vecN<T> 또는
ptr<AS,vecN<T,AM>> I,J,K는 x , y , z ,
w 중 하나AM은 read 또는 read_write |
e. IJK: vec3<T> | 첫 번째 성분 e.I, 두 번째 e.J, 세 번째
e.K로 3성분 벡터 생성z 는 N이 3 또는 4일 때만 유효w 는 N이 4일 때만 유효e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함. |
e: vecN<T> 또는
ptr<AS,vecN<T,AM>> I,J,K는 r , g , b ,
a 중 하나AM은 read 또는 read_write |
e. IJK: vec3<T> | 첫 번째 성분 e.I, 두 번째 e.J, 세 번째
e.K로 3성분 벡터 생성b 는 N이 3 또는 4일 때만 유효a 는 N이 4일 때만 유효e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함. |
e: vecN<T> 또는
ptr<AS,vecN<T,AM>> I,J,K,L는 x , y ,
z , w 중 하나AM은 read 또는 read_write |
e. IJKL:
vec4<T> | 첫 번째 e.I, 두 번째 e.J, 세 번째 e.K,
네 번째 e.L로 4성분 벡터 생성z 는 N이 3 또는 4일 때만 유효w 는 N이 4일 때만 유효e가 포인터면 먼저 간접 참조를 적용하고 로드 규칙을 호출함. |
e: vecN<T> 또는
ptr<AS,vecN<T,AM>> I,J,K,L는 r , g ,
b , a 중 하나AM은 read 또는 read_write |
e. IJKL:
vec4<T> | 첫 번째 e.I, 두 번째 e.J, 세 번째 e.K,
네 번째 e.L로 4성분 벡터 생성b 는 N이 3 또는 4일 때만 유효a 는 N이 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> |
e의 i번째 열 벡터가 결과값임
i가 [0,C-1] 범위를 벗어나면:
|
e: matCxR<T> i: i32 또는 u32 T은 추상 타입 i가 상수 표현식일 때 | e[i]: vecR<T> |
e의 i번째 열 벡터가 결과값임
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 |
결과는 배열 값 e의 i번째 요소 값임.
i가 [0,N-1] 범위를 벗어나면:
|
e: array<T,N> i: i32 또는 u32 T은 추상 타입 i가 상수 표현식일 때 | e[i] : T |
결과는 배열 값 e의 i번째 요소 값임.
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는 구조체 타입 M은 S의 멤버 식별자 이름이고 타입은 T e: S | e.M: T | 결과는 구조체 값 e에서 이름이 M인 멤버의 값임. |
전제조건 | 결론 | 설명 |
---|---|---|
S는 구조체 타입 M은 S의 멤버 식별자 이름이고 타입은 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
| 논리 부정.
e가 false 면 결과는 true , e가 true 면 결과는
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". e1과 e2가 모두 true면 true; e1이 true일 때만 e2 평가. |
e1: T e2: T T는 bool 또는 vecN<bool> | e1 | e2: T
| 논리 "or". 성분별 처리됨. T가 벡터일 때. e1과 e2 모두 평가. |
e1: T e2: T T는 bool 또는 vecN<bool> | e1 & e2: T
| 논리 "and". 성분별 처리됨. T가 벡터일 때. e1과 e2 모두 평가. |
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) 쌍 중 다음을 제외한 집합임:
|
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) 쌍 중 다음을 제외한 집합임:
|
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가 부동소수점 타입일 때, 결과는 T가 부동소수점 타입일 때, 스칼라 정의역은 모든 확장 실수 (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> | 행렬 덧셈: 결과는 성분별로 계산되며, 결과의 열 i는 e1[i] + e2[i] |
e1 - e2: matCxR<T>
| 행렬 뺄셈: 결과는 성분별로 계산되며, 결과의 열 i는 e1[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> | 선형대수 행렬-열벡터 곱:
결과의 성분 i는
dot (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 T는 S 또는 vecN<S> TB는 T가 벡터일 때 vecN<bool>이고, 아니면 bool | e1 == e2: TB
| 동등 비교. 성분별 처리, T가 벡터일 때. |
e1: T e2: T S는 AbstractInt, AbstractFloat, bool, i32, u32, f32, 또는 f16 T는 S 또는 vecN<S> TB는 T가 벡터일 때 vecN<bool>이고, 아니면 bool | e1 != e2: TB
| 비동등 비교. 성분별 처리, T가 벡터일 때. |
e1: T e2: T S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T가 S, 또는 vecN<S> TB는 T가 벡터일 때 vecN<bool>이고, 아니면 bool | e1 < e2: TB
| 미만 비교. 성분별 처리, T가 벡터일 때. |
e1: T e2: T S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T가 S, 또는 vecN<S> TB는 T가 벡터일 때 vecN<bool>이고, 아니면 bool | e1 <= e2: TB
| 이하 비교. 성분별 처리, T가 벡터일 때. |
e1: T e2: T S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T가 S, 또는 vecN<S> TB는 T가 벡터일 때 vecN<bool>이고, 아니면 bool | e1 > e2: TB
| 초과 비교. 성분별 처리, T가 벡터일 때. |
e1: T e2: T S가 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T가 S, 또는 vecN<S> TB는 T가 벡터일 때 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 T는 S 또는 vecN<S> TS는 T가 S일 때 u32, 아니면 vecN<u32> | e1 << e2: T
|
왼쪽 시프트(시프트 값은 구체):
e1을 왼쪽으로 시프트하며, 하위 비트에는 0을 채우고, 상위 비트는 버린다. 시프트할 비트 수는 e2의 값, e1의 비트 폭으로 나눈 나머지.
셰이더 실행 시작 전에 e1, e2가 모두 정해진 경우, 결과값이 오버플로우되어선 안 됨:
성분별 처리, T가 벡터일 때. |
e1: T e2: TS T는 AbstractInt 또는 vecN<AbstractInt> TS는 T가 AbstractInt일 때 u32, 아니면 vecN<u32> | e1 << e2: T
|
왼쪽 시프트(시프트 값은 추상):
e1을 왼쪽으로 시프트하며, 하위 비트에는 0을 채우고, 상위 비트는 버린다. 시프트할 비트 수는 e2의 값이다. e1의 상위 e2+1 비트는 반드시 동일해야 한다. 아니면 오버플로우가 발생한다. 참고: 이 조건은 버려지는 비트가 원래 값의 부호 비트와 같아야 하며, 결과값의 부호 비트와도 같아야 함을 의미한다. 성분별 처리, T가 벡터일 때. |
e1: T e2: TS S는 i32 또는 u32 T는 S 또는 vecN<S> TS는 T가 S일 때 u32, 아니면 vecN<u32> | e1 >> e2: T |
오른쪽 시프트(시프트 값은 구체):
e1을 오른쪽으로 시프트하며, 하위 비트는 버린다. S가 부호 없는 타입이면, 상위 비트에 0을 채운다. S가 부호 있는 타입이면:
시프트할 비트 수는 e2의 값, e1의 비트 폭으로 나눈 나머지. e2가 e1의 비트 폭 이상이면:
성분별 처리, T가 벡터일 때. |
e1: T e2: TS T는 AbstractInt 또는 vecN<AbstractInt> TS는 T가 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가 잘못된 메모리 참조라면, 결과 포인터도 잘못된 메모리 참조입니다. |
8.14. 간접 참조 표현식
간접 참조 연산자는 포인터를 해당 참조로 변환합니다.
전제조건 | 결론 | 설명 |
---|---|---|
p: ptr<AS,T,AM> |
* p: ref<AS,T,AM>
|
결과는 포인터 값 p와 동일한 메모리 뷰에 대응하는 참조 값입니다.
p가 잘못된 메모리 참조라면, 결과 참조도 잘못된 메모리 참조입니다. |
8.15. 값 선언을 위한 식별자 표현식
전제조건 | 결론 | 설명 |
---|---|---|
c는 식별자이며 해석시 스코프 내 const 선언(타입 T)으로 해석됨 | c: T | 초기화자 표현식으로 계산된 값이 결과입니다. 표현식은 상수 표현식이며, 셰이더 생성 시점에 평가됩니다. |
c는 식별자이며 해석시 스코프 내 override 선언(타입 T)으로 해석됨 | c: T |
파이프라인 생성에서 상수 ID에 대해 값이 지정된 경우,
결과는 해당 값입니다.
이 값은 파이프라인 인스턴스마다 다를 수 있습니다.
그렇지 않으면, 결과는 초기화자 표현식으로 계산된 값입니다. 파이프라인 오버라이드 상수는 모듈 범위에 나타나므로, 셰이더 실행 전 평가됩니다. 참고: API 호출에서 초기 값이 지정되지 않고
|
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 | tg _template_args_start e1, ..., eN _template_args_end : AllTypes |
각 타입 생성자는 요구하는
템플릿 파라미터 규칙 및 결과 타입 결정 방식을 정의합니다.
표현식 e1~eN은 해당 타입 생성자의 템플릿 파라미터입니다. 예를 들어 § 6.9 미리 선언된 타입 및 타입 생성자 요약 참고. 참고: 두 변형의 차이는 eN 뒤에 쉼표가 있느냐 뿐입니다. |
tg _template_args_start e1, ..., eN, _template_args_end : AllTypes |
8.18. 표현식 문법 요약
식별자가 call_phrase의 첫 번째 토큰일 때, 다음 중 하나입니다:
선언 및 스코프 규칙에 따라 이 이름들은 항상 구분됩니다.
참고: call_expression 규칙은 호출 표현식에 타입 검사가 적용되도록 보장합니다.
expression ( ','
expression ) * ','
?
'['
expression ']'
component_or_swizzle_specifier ?
| multiplicative_expression multiplicative_operator unary_expression
| additive_expression additive_operator multiplicative_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
binary_and_expression '&'
unary_expression
| short_circuit_or_expression '||'
relational_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개 이상의 문장 시퀀스입니다. 그 안에 선언이 포함될 경우, 해당 식별자는 다음 문장 시작부터 복합 문장 끝까지 스코프 내에 존재합니다.
continuing_compound_statement는 복합 문장의 특수 형태로, continuing 문장의 본문을 구성하며, 마지막에 break-if 문장을 선택적으로 포함할 수 있습니다.
9.2. 대입 문장
대입은 표현식을 평가하고, 옵션에 따라 메모리에 저장하여 변수의 내용을 갱신합니다.
lhs_expression ( '='
| compound_assignment_operator ) expression
| '_'
'='
expression
연산자 토큰 왼쪽의 텍스트를 좌변, 연산자 토큰 오른쪽의 표현식을 우변이라 합니다.
9.2.1. 단순 대입
대입이
단순 대입이 되는 경우는,
좌변이 표현식이고, 연산자가 등호('='
) 토큰일
때입니다.
이 경우, 우변의 값이 좌변이 참조하는 메모리에
기록됩니다.
전제조건 | 문장 | 설명 |
---|---|---|
e: T, T가 구체 생성가능 타입, r: ref<AS,T,AM>, AS가 쓰기 가능한 주소 공간, 접근 모드 AM이 write 또는 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를 평가합니다.
참고: 평가 결과값은 저장되지 않습니다.
|
가짜 대입은 다음과 같은 경우에 유용합니다:
-
값을 반환하는 함수를 호출하지만, 결과값이 필요 없음을 명확히 표현할 때.
-
정적으로 접근하여 변수를 셰이더의 리소스 인터페이스의 일부로 포함시킬 때.
참고: 버퍼 변수의 저장 타입이 생성가능하지 않을 수도 있습니다(예: atomic 타입이나 런타임 크기 배열 포함). 이런 경우에는 변수 내용의 포인터를 대신 사용하세요.
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. 복합 대입
대입이 복합 대입이 되는 경우는, 좌변이 표현식이고, 연산자가 복합 대입 연산자 중 하나일 때입니다.
각 문장의 타입 요구사항, 의미론, 동작은 복합 대입이 아래 표와 같이 확장되는 것처럼 정의됩니다. 단,
-
참조식 e1은 한 번만 평가되고,
-
e1의 참조 타입은 read_write 접근 모드이어야 합니다.
문장 | 확장 |
---|---|
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가 됨. }
e1+=
e2;
아래와 같이 다시 쓸 수 있습니다.
여기서 식별자{ let p = &(
e1); *p = *p + (
e2); }
p
는 프로그램 내 다른 식별자와 중복되지 않게 선택합니다.
ev아래와 같이 다시 쓸 수 있습니다.[
c] +=
e2;
여기서{ let p = &(
ev); let c0 =
c; (*p)[c0] = (*p)[c0] + (
e2); }
c0
와 p
식별자는 프로그램 내 다른 식별자와 중복되지 않게 선택합니다.
9.3. 증감 문장
증가 문장은 변수의 값에 1을 더합니다. 감소 문장은 변수의 값에서 1을 뺍니다.
해당 표현식은 반드시 구체 정수 스칼라 저장 타입 및 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)과 동일 |
9.4. 제어 흐름
제어 흐름 문장은 프로그램을 비순차적으로 실행시킬 수 있습니다.
9.4.1. if 문장
if 문장은 조건 표현식의 평가에 따라 최대 하나의 복합 문장을 조건부로 실행합니다.
if
문은 if
절, 0개 이상의 else if
절, 선택적 else
절로 구성됩니다.
'else'
'if'
expression compound_statement
'else'
compound_statement
타입 규칙 전제조건:
각 if
및 else if
절의 표현식은 반드시 bool 타입이어야 합니다.
if
문장은 다음과 같이 실행됩니다:
-
if
절의 조건을 평가합니다. 결과가true
이면, 제어가 조건 표현식 바로 다음의 첫 번째 복합 문장으로 이동합니다. -
그렇지 않으면 텍스트 순서대로 다음
else if
절(존재하는 경우)의 조건을 평가하고, 결과가true
이면 해당 복합 문장으로 제어가 이동합니다.-
이 동작은 모든
else if
절에 대해 조건이true
가 될 때까지 반복합니다.
-
-
어떤 조건도
true
가 아니면,else
절에 연결된 복합 문장으로 제어가 이동합니다(존재하는 경우).
9.4.2. switch 문장
switch 문장은 선택자 표현식의 평가에 따라 여러 case 절 또는 default 절 중 하나로 제어를 이동시킵니다.
attribute * 'switch'
expression switch_body
attribute * '{'
switch_clause + '}'
'case'
case_selectors ':'
? compound_statement
'default'
':'
? compound_statement
case_selector ( ','
case_selector ) * ','
?
'default'
case 절은 'case'
토큰 다음에
쉼표로 구분된 case 선택자
리스트와 복합 문장 형태의 본문이
옵니다.
default-alone 절은
'default'
토큰 뒤에 복합 문장 형태의 본문이
옵니다.
default 절은 다음 중 하나입니다:
-
default-alone 절인 경우
각 switch 문장은 반드시 하나의 default 절를 가져야 합니다.
'default'
토큰은 하나의 case_selector 리스트에 두 번 이상 나타나면 안 됩니다.
타입 규칙 전제조건: 하나의 switch 문장에 대해 선택자 표현식과 모든 case 선택자 표현식은 반드시 동일한 구체 정수 스칼라 타입이어야 합니다.
case_selectors의 표현식들은 반드시 const-expression이어야 합니다.
동일 switch 문장 내에서 서로 다른 case 선택자 표현식이 동일한 값을 가지면 안 됩니다.
선택자 값이 case_selector 리스트의 어떤 표현식과 같으면, 해당 case 절의 본문으로 제어가 이동합니다. 선택자 값이 어떤 case 선택자 값과도 같지 않으면 default 절의 본문으로 제어가 이동합니다.
절 본문의 끝에 도달하면, 제어는 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 ; } }
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 문장은 loop 본문을 반복 실행합니다; loop 본문은 복합 문장으로 지정됩니다. loop 본문의 각 실행을 반복(iteration)이라고 합니다.
이 반복은 break 또는 return 문장에 의해 중단될 수 있습니다.
옵션으로, loop 본문의 마지막 문장은 continuing 문장일 수 있습니다.
loop가 무한 반복될 경우 동적 오류가 발생합니다. 이 경우 반복이 조기에 종료되거나, 기타 비지역적 효과가 발생하거나 디바이스 손실이 발생할 수 있습니다.
loop 본문 내 문장이 선언이면, 복합 문장의 스코프 및 수명 규칙을 따릅니다. 즉, loop 본문은 문장 시퀀스이고, 그 중 하나가 선언이면 해당 선언의 스코프는 다음 문장부터 loop 본문 끝까지입니다. 선언은 도달 시마다 실행되어 변수 또는 값의 새 인스턴스를 생성 및 초기화합니다.
참고: loop 문장은 특수 구조이므로, 일반적으로 for
나
while
문장을 사용할 수 있습니다. loop 문장은 다른 셰이더 언어와 가장 큰 차이점 중 하나입니다.
이 설계는 컴파일된 코드에서 흔히 볼 수 있는 반복 패턴을 직접적으로 표현합니다. 특히, loop body 끝에 반복 갱신문을 배치하면 loop 본문에서 정의된 값을 자연스럽게 사용할 수 있습니다.
- <1> 초기화는 loop 앞에 나열됩니다.
var a : i32= 2 ; let step : i32= 1 ; for ( var i : i32= 0 ; i < 4 ; i += step ) { if ( i % 2 == 0 ) { continue ; } a *= 2 ; }
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 ; }
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 ; } }
- <2> continue 구문은
loop
끝에 배치됩니다.
9.4.4. for 문장
attribute * 'for'
'('
for_header ')'
compound_statement
for_init ? ';'
expression ? ';'
for_update ?
for 문장은
for (초기화; 조건; 갱신부) { 본문 }
형태를 가지며, 동일한 본문
을 갖는 loop 문장의 구문적 설탕입니다.
추가적으로:
-
초기화
가 비어있지 않다면, 첫 반복 전에 추가 스코프 내에서 실행됩니다. 초기화 내 선언의 스코프는 loop 본문 끝까지 확장됩니다. -
타입 규칙 전제조건: 조건이 비어있지 않으면, 반드시 bool 타입의 표현식이어야 합니다.
-
조건이 있을 경우, loop 본문 실행 직전에 평가합니다. 조건이 false이면 § 9.4.6 Break 문장이 실행되어 loop 실행이 종료됩니다. 이 확인은 각 반복 시작 시 수행됩니다.
-
-
갱신부
가 비어있지 않으면, loop 본문 끝의 continuing 문장이 됩니다.
for 반복문의 초기화
는 반복문 실행 전 한 번만 실행됩니다.
선언이 초기화에 등장하면,
해당 식별자는 스코프 내에 본문 끝까지 존재합니다.
본문 내 선언과 달리, 초기화 선언은 각 반복마다 재초기화되지 않습니다.
조건
, 본문
, 갱신부
는 해당 순서대로 실행되어 loop 반복을 형성합니다.
본문
은 복합
문장의 특수 형태입니다.
본문 내 선언의 식별자는 스코프 내에 다음 문장 시작부터 본문 끝까지
존재합니다.
선언은 매번 도달할 때 실행되므로, 각 반복마다 변수나 상수의 새 인스턴스를 생성 및 재초기화합니다.
var a : i32= 2 ; for ( var i : i32= 0 ; i < 4 ; i ++ ) { if a == 0 { continue ; } a = a + 2 ; }
변환 결과:
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 문장
attribute * 'while'
expression compound_statement
while 문장은 조건에 따라 반복되는 반복문입니다. 각 반복 iteration 시작 시, bool 조건이 평가됩니다. 조건이 false일 경우 while 반복문은 실행을 종료합니다. 그렇지 않으면 나머지 반복이 실행됩니다.
타입 규칙 전제조건: 조건은 반드시 bool 타입이어야 합니다.
while 반복문은 loop 또는 for 문장의 구문적 설탕으로 볼 수 있습니다. 아래 형태의 문장은 모두 동일합니다:
-
while
조건{
본문_문장}
-
loop { if !
조건{break;}
본문_문장}
-
for (;
조건;) {
본문_문장}
while 반복문이 무한 반복될 경우 동적 오류가 발생합니다. 이 경우 반복이 조기에 종료되거나, 기타 비지역적 효과가 발생하거나 디바이스 손실이 발생할 수 있습니다.
9.4.6. break 문장
'break'
break 문장은 가장 가까운 감싸는 loop 또는 switch 문장의 본문 바로 뒤로 제어를 이동시켜, 반복 또는 switch 문장 실행을 종료합니다.
break
문장은 반드시 loop, for, while, switch 문장 내에서만 사용할 수 있습니다.
break
문장은 반복문의 continuing 문장을 빠져나가도록 배치되어서는 안 됩니다.
대신 break-if 문장을 사용하세요.
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'
expression ';'
break-if 문장은 bool 조건을 평가합니다. 조건이 true이면, 제어는 가장 가까운 loop 문장의 본문 바로 뒤로 이동하여 해당 loop 실행을 종료합니다.
타입 규칙 전제조건: 조건은 반드시 bool 타입이어야 합니다.
참고: break-if 문장은 continuing 문장 본문의 마지막 문장으로만 등장할 수 있습니다.
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'
continue 문장은 가장 가까운 loop에서 제어를 다음 위치로 이동시킵니다:
-
loop 본문 끝에 continuing 문장이 있으면 그 continuing 문장으로 이동합니다.
-
그렇지 않으면 loop 본문의 첫 문장으로 되돌아가서 다음 반복을 시작합니다.
continue
문장은 반드시 loop, for, while 문장에서만 사용할 수 있습니다.
continue
문장은 반드시 감싸는 continuing 문장으로 제어를 이동시키는 위치에 올 수 없습니다.
(continuing 문장으로 이동하는 경우 forward 분기입니다.)
continue
문장은 반드시 targeted continuing 문장에서 사용하는 선언을 건너뛰게 배치되어서는
안 됩니다.
참고: continue
는 continuing
문장
내에서, 그 continuing 문장 내 또 다른 loop에서만 제어 흐름을 이동시킬 때 사용할 수 있습니다. 즉, 현재 실행 중인 continuing 문장 시작으로 제어를 이동시키는 용도로는
사용할 수 없습니다.
var i : i32= 0 ; loop { if i >= 4 { break ; } if i % 2 == 0 { continue ; } // <3> let step : i32= 2 ; continuing { i = i + step ; } }
- <3>
continue
는continuing
구문에서 사용하는step
선언을 건너뛰므로 잘못되었습니다
9.4.9. continuing 문장
'continuing'
continuing_compound_statement
continuing 문장은 loop 반복 끝에 실행되는 복합 문장을 지정합니다. 이 구문은 선택적입니다.
복합 문장에는 어느 중첩 단계에서도 return이 올 수 없습니다.
9.4.10. return 문장
'return'
expression ?
return 문장은 현재 함수의 실행을 종료합니다. 함수가 엔트리 포인트일 경우, 현재 셰이더 호출이 종료됩니다. 그렇지 않으면, 현재 함수 호출의 호출 위치 평가 이후의 다음 표현식 또는 문장으로 실행이 이어집니다.
함수에 반환 타입이 없으면 return 문장은 선택사항입니다. 이런 함수에서 return 문을 제공할 경우 반드시 값을 제공해선 안 됩니다. 그렇지 않으면, 반드시 반환값 표현식이 있어야 하며, 이를 반환값이라 합니다. 이 경우 해당 함수 호출 위치의 평가 결과가 반환값이 됩니다. 반환값의 타입은 함수의 반환 타입과 반드시 일치해야 합니다.
9.4.11. discard 문장
discard 문장은 현재 호출을 helper 호출로 변환하여 프래그먼트를 버립니다.
discard
문장은 반드시 fragment 셰이더 단계에서만 사용할 수 있습니다.
정확히 말하면 discard
문장을 실행하면 다음이 발생합니다:
-
현재 호출이 helper 호출로 변환되고,
-
현재 프래그먼트가 GPURenderPipeline의 다운스트림에서 처리되지 않게 됩니다.
discard
문장 실행 전에 실행된 문장만 관찰 가능한 효과를 가집니다.
참고: discard
문장은 fragment 단계의 어떤
함수에서든 실행될 수 있으며, 효과는 같습니다:
해당 프래그먼트는 버려집니다.
@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. 함수 호출 문장
함수 호출 문장은 함수 호출을 실행합니다.
호출된 함수에 must_use 속성이 있으면 셰이더 생성 오류가 발생합니다.
참고: 함수가 값을 반환하고, must_use 속성이 없다면 그 값은 무시됩니다.
9.6. 문장 문법 요약
statement 규칙은 함수 본문 내 대부분 위치에서 사용 가능한 문장에 일치합니다.
추가적으로, 특정 문장은 아주 특정한 문맥에서만 사용될 수 있습니다:
9.7. 문장 동작 분석
9.7.1. 규칙
제어 흐름에 영향을 주는 일부 문장은 특정 문맥에서만 유효합니다. 예를 들어, continue 는 loop, for, 또는 while 안에서만 유효합니다. 또한, 균일성 분석(see § 15.2 균일성)에서는 제어 흐름이 여러 방식으로 문장을 빠져나갈 수 있는지 파악해야 합니다.
이 두 목적은 문장 실행 동작 요약 시스템으로 달성됩니다. 동작 분석은 각 문장을 평가 완료 후 실행이 이어지는 가능한 방식을 집합으로 요약합니다. 값과 표현식의 타입 분석처럼, 동작 분석도 하향식(bottom up)으로 진행됩니다: 먼저 기본 문장들의 동작을 결정한 뒤, 결합 규칙을 적용해 상위 구조의 동작을 결정합니다.
동작(behavior)은 집합이며, 요소는 다음일 수 있습니다:
-
Return
-
Break
-
Continue
-
Next
각 요소는 복합 문장을 탈출하는 방법(키워드를 통해 또는 "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 | B1 ∪ B2 |
loop {s1 continuing {s2}} |
s1: B1 s2: B2 {Continue, Return}이 B2에 없음 Break가 (B1 ∪ B2)에 없음 | (B1 ∪ B2)∖{Continue, Next} |
s1: B1 s2: B2 {Continue, Return}이 B2에 없음 Break가 (B1 ∪ B2)에 있음 | (B1 ∪ B2 ∪ {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
반복문이 초기화 또는 갱신 문장이 없는 경우에 발생합니다.
이 분석을 위해:
-
for
반복문은 desugar 처리됨(see § 9.4.4 For 문장) -
while
반복문은 desugar 처리됨(see § 9.4.5 While 문장) -
loop {s}
는loop {s continuing {}}
로 취급함 -
else
없는if
문장은 빈 else 분기를 가진 것처럼 취급(Next가 동작에 추가됨) -
else if
가 있는if
문장은 중첩된 단순if/else
로 취급 -
default
로 시작하는 switch_clause는case _:
로 시작하는 switch_clause와 동일하게 동작함
각 내장 함수는 {Next} 동작을 가집니다. 위 표에 없는 연산자 적용도 동일 피연산자를 갖는 함수 호출과 동일하게 {Next} 동작을 가집니다.
함수의 동작은 반드시 위 규칙을 만족해야 합니다.
참고: 표현식의 동작을 분석할 필요는 없습니다. 항상 {Next}이거나, 이전에 분석된 함수가 오류를 발생시켰기 때문입니다.
9.7.2. 참고
이 절은 참고용이며 비규범적입니다.
동작 분석은 다음과 같은 경우 프로그램을 거부할 수 있습니다 (위의 요구사항을 재진술):
-
함수 본문(일반 문장으로 취급)이 {Next, Return}에 포함되지 않은 동작을 가질 때.
-
반환 타입이 있는 함수의 본문 동작이 {Return}이 아닐 때.
-
continuing 블록의 동작에 Continue 또는 Return이 포함될 때.
-
명백히 무한 루프인 경우 동작 집합이 비어 있으므로, 이는 유효하지 않습니다.
이 분석은 호출 그래프를 하향식(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} }
fn if_example () { var a : i32= 0 ; loop { if a == 5 { break ; // 동작: {Break} } // if 복합 문장 전체 동작: {Break, Next}, // if에 암묵적 빈 else가 있음 a = a + 1 ; // 유효, 이전 문장 동작에 "Next" 있음 } }
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} } }
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}
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 ; // 동작 {Next}. } // 유효하지 않음, 전체 루프 동작은 { }임. }
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로 치환 }
fn redundant_continue_with_continuing () { var a : i32; loop { if a == 5 { break ; } continue ; // 유효. 불필요하지만, 다음 문장으로 분기. continuing { a = a + 1 ; } } }
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"가 추가됩니다.
fn missing_return () -> i32{ var a : i32= 0 ; if a == 42 { return a ; // 동작: {Return} } // 동작: {Next, Return} } // 오류: Next는 반환 타입이 있는 함수 본문에 유효하지 않음
fn continue_out_of_loop () { var a : i32= 0 ; if a > 0 { continue ; // 동작: {Continue} } // 동작: {Next, Continue} } // 오류: Continue는 함수 본문에 유효하지 않음
continue
를 break
로 바꿔도 같은 이유로 유효하지 않습니다.
10. 단언문
단언문은 bool 조건이 충족되는지 확인하는 검사입니다.
WGSL은 한 가지 단언문을 정의합니다: const 단언문.
'const_assert'
expression
타입 규칙 전제조건: 해당 표현식은 반드시 bool 타입이어야 합니다.
10.1. Const 단언문
Const 단언문은 단언문으로,
해당 표현식이 false
로 평가되면 셰이더 생성 오류를 발생시킵니다.
표현식은 반드시 const-expression이어야
합니다.
이 문장은 셰이더에서 정적 접근
조건을 만족할 수 있지만,
그 외에는 컴파일된 셰이더에 아무런 영향을 주지 않습니다.
Const 단언문은 모듈 범위 또는 함수 범위 문장으로 나타날 수 있습니다.
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. 함수
함수는 호출 시 계산 작업을 수행합니다.
함수는 다음 방식 중 하나로 호출됩니다:
-
함수 호출 표현식을 평가하여. § 8.10 함수 호출 표현식 참고.
-
함수 호출 문장을 실행하여. § 9.5 함수 호출 문장 참고.
-
엔트리 포인트 함수는 WebGPU 구현체가 파이프라인의 셰이더 단계 작업을 수행할 때 호출됩니다. § 13 엔트리 포인트 참고.
WGSL의 함수는 정의 순서에 제한이 없으며, 사용 후에 정의되어도 됩니다. 따라서 함수 원형 또는 전방 선언이 필요 없고, 할 수 있는 방법도 없습니다.
함수에는 두 가지 종류가 있습니다:
-
내장 함수는 WGSL 구현체에서 제공하며, 항상 WGSL 모듈에서 사용할 수 있습니다. § 17 내장 함수 참고.
-
사용자 정의 함수는 WGSL 모듈에 선언됩니다.
11.1. 사용자 정의 함수 선언
함수 선언은 사용자 정의 함수를 생성하며, 다음을 지정합니다:
-
선택적인 속성 집합.
-
함수 이름.
-
형식 매개변수 목록: 0개 이상의 형식 매개변수 선언의 순서이며, 각 매개변수는 속성을 가질 수 있고, 쉼표로 구분하며, 괄호로 둘러싸입니다.
-
선택적 반환 타입 (속성 적용 가능).
-
함수 본문. 함수가 호출될 때 실행될 문장 집합입니다.
함수 선언은 반드시 모듈 범위에서만 등장해야 합니다. 함수 이름은 프로그램 전체에서 스코프 내에 존재합니다.
참고: 각 사용자 정의 함수는 오직 하나의 오버로드만 가집니다.
형식 매개변수 선언은 함수 호출 시 제공되어야 하는 값의 식별자 이름과 타입을 지정합니다. 형식 매개변수는 속성을 가질 수 있습니다. § 11.2 함수 호출 참고. 식별자의 스코프는 함수 본문입니다. 하나의 함수에 대해 두 형식 매개변수가 같은 이름을 가지면 안 됩니다.
참고: 일부 내장 함수는 추상 숫자 타입 매개변수를 허용할 수 있습니다. 하지만, 이 기능은 사용자 선언 함수에서는 현재 지원되지 않습니다.
반환 타입이 지정된 경우, 반드시 생성가능 타입이어야 합니다.
WGSL에서 함수 선언에 적용할 수 있는 속성은 다음과 같습니다:
WGSL에서 함수 매개변수와 반환 타입에 적용할 수 있는 속성은 다음과 같습니다:
'fn'
ident '('
param_list ? ')'
( '->'
attribute * template_elaborated_ident ) ?
// 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. 함수 호출
함수 호출은 함수를 실행시키는 문장 또는 표현식입니다.
함수 호출이 포함된 함수는 호출 함수 또는 호출자라 합니다. 호출되는 함수는 피호출 함수 또는 피호출자라 합니다.
함수 호출은 다음을 포함합니다:
-
피호출 함수 이름 지정
-
괄호로 묶인, 쉼표로 구분된 인자 값 표현식 목록 제공
함수 호출은 반드시 피호출 함수의 형식 매개변수 개수와 동일한 개수의 인자를 제공해야 합니다. 각 인자 값은 반드시 해당 위치의 형식 매개변수와 동일한 타입으로 평가되어야 합니다.
함수 호출 시 요약:
피호출 함수가 return하는 방식:
-
내장 함수는 작업 완료 시 return한다.
함수 호출이 실행될 때 상세 절차:
-
함수 호출 인자 값 평가(평가 순서는 좌→우).
-
피호출 함수가 사용자 정의 함수라면, 피호출 함수의 함수 범위 변수마다 메모리 할당.
-
초기화는 § 7.3 var 선언에서 설명한 대로 수행.
-
-
피호출 함수의 형식 매개변수 값은 함수 호출 인자 값을 위치로 매칭하여 결정. 예: 첫 번째 형식 매개변수는 호출 위치의 첫 번째 인자 값을 가짐.
-
피호출 함수가 return할 때까지 실행.
-
호출 함수로 제어가 돌아오고, 피호출 함수 실행이 중지 해제됨. 피호출 함수가 값을 반환하면 그 값이 함수 호출 표현식의 값이 됨.
함수 호출 위치는 호출 위치라 하며, call_phrase 문법 규칙의 파싱 인스턴스에서 첫 번째 토큰 위치를 의미합니다. 호출 위치는 동적 컨텍스트입니다. 따라서 동일한 텍스트 위치가 여러 호출 위치를 나타낼 수 있습니다.
참고: fragment 셰이더에서 모든 쿼드가 discard될 경우, 함수 호출이 return하지 않을 수 있습니다. 이런 경우 제어가 호출 함수로 돌아오지 않습니다.
11.3. const
함수
const 속성으로 선언된 함수는 셰이더 생성 시점에 평가될 수 있습니다. 이러한 함수는 const 함수라 부릅니다. 이 함수 호출은 const-expression의 일부가 될 수 있습니다.
함수에 const-expression이 아닌 표현식이나, 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에 대해, 루트 식별자는 다음과 같이 찾은 원본 변수 또는 형식 매개변수 입니다:
-
E가 변수로 해결되는 식별자라면, 루트 식별자는 해당 변수입니다.
-
E가 포인터 타입의 형식 매개변수로 해결되는 식별자라면, 루트 식별자는 해당 형식 매개변수입니다.
-
E가
(
E2)
,&
E2,*
E2, 또는 E2[
Ei]
형태라면, 루트 식별자는 E2의 루트 식별자입니다. -
E가 E2.swiz 형태의 벡터 접근 표현식이고 swiz가 swizzle 이름이면, 루트 식별자는 E2의 루트 식별자입니다.
-
E가 E2.member_name 형태의 구조체 접근 표현식이면, 루트 식별자는 E2의 루트 식별자입니다.
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와의 인터페이스 지정 등 다양한 목적으로 사용됩니다.
일반적으로 언어 관점에서 속성은 타입 및 의미론 검사 목적에서는 무시될 수 있습니다. 또한 속성 이름은 컨텍스트 의존 이름이며, 일부 속성 파라미터도 컨텍스트 의존 이름입니다.
'@'
ident_pattern_token
argument_expression_list ?
| id_attr
속성 설명에서 명시적으로 허용하지 않는 한, 하나의 객체 또는 타입에 대해 동일 속성을 두 번 이상 지정하면 안 됩니다.
12.1. align
'@'
'align'
'('
expression ','
?
')'
설명 |
구조체 멤버의 메모리 배치를 제약합니다.
이 속성은 해당 구조체 타입 값이 메모리에서 어떻게 나타날 수 있는지에 영향을 미치며, 구조체 자체와 그 구성 멤버가 나타날 수 있는 바이트 주소를 제약합니다. align( n) 이 타입 T의 S 멤버에 적용되고,
S가 주소 공간 AS의 변수에 대한 저장 타입이 될 수 있을 때,
AS가 uniform이 아니라면,
n은 다음을 만족해야 합니다:
n = k × RequiredAlignOf(T,AS) 여기서 k는 양의 정수입니다. 정렬 및 크기 규칙은 상호 재귀적입니다. 하지만 위 제약은 중첩 타입의 필수 정렬에만 의존하므로 잘 정의됩니다. 타입의 중첩 깊이는 유한합니다. § 14.4 메모리 레이아웃 참고. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다. 반드시 양수여야 합니다. 반드시 2의 거듭제곱이어야 합니다. |
12.2. binding
'@'
'binding'
'('
expression ','
?
')'
설명 | 바인드 group에서 리소스의 바인딩 번호를 지정합니다. § 13.3.2 리소스 인터페이스 참고. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다. 반드시 0 이상이어야 합니다. |
12.3. blend_src
'@'
'blend_src'
'('
expression ','
?
')'
설명 |
dual_source_blending
기능이 활성화된 경우 fragment 출력의 일부를
지정합니다.
§ 13.3.1.3 입출력 위치 참고.
반드시 location 속성이 있는 구조체 타입의 멤버에만 적용해야 합니다. 반드시 숫자 스칼라 또는 숫자 벡터 타입 객체 선언에만 적용해야 합니다. 포함되면 안 됨 셰이더 단계 입력에. 포함되면 안 됨 셰이더 단계 출력에, 단 fragment 셰이더 단계는 예외. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32이고 값이 0 또는 1 이어야 합니다.
|
12.4. builtin
'@'
'builtin'
'('
builtin_value_name ','
? '')
설명 | 연결된 객체가 지정된 토큰으로 표시되는 내장 값임을 지정합니다. § 13.3.1.1 내장 입력 및 출력 참고. |
파라미터 | 반드시 내장 값 이름-토큰이어야 하며, 내장 값이어야 합니다. |
12.5. const
'@'
'const'
설명 |
해당 함수가 const
함수로 사용될 수 있음을 지정합니다.
이 속성은 사용자 정의 함수에는 적용할 수 없습니다.
반드시 함수 선언에만 적용해야 합니다. 참고: 이 속성은 어떤 내장 함수가 const-expression에서 사용될 수 있는지 표기 관례로 사용됩니다. |
파라미터 | 없음 |
12.6. diagnostic
'@'
'diagnostic'
diagnostic_control
설명 |
범위
진단 필터를 지정합니다. § 2.3 진단 참고.
하나의 구문 형태에 둘 이상의 diagnostic 속성을 지정할 수 있지만, 반드시 서로 다른 트리거 규칙을 지정해야 합니다. |
파라미터 |
첫 번째 파라미터는 severity_control_name입니다.
두 번째 파라미터는 diagnostic_rule_name 토큰으로, 트리거 규칙을 지정합니다. |
12.7. group
'@'
'group'
'('
expression ','
?
')
설명 | 리소스의 바인딩 그룹을 지정합니다. § 13.3.2 리소스 인터페이스 참고. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다. 반드시 0 이상이어야 합니다. |
12.8. id
'@'
'id'
'('
expression ','
?
')
설명 |
파이프라인
오버라이더블 상수에 대한 대체 이름으로 숫자 식별자를 지정합니다.
반드시 override 선언의 스칼라 타입에만 적용해야 합니다. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다. 반드시 0 이상이어야 합니다. |
12.9. interpolate
'@'
'interpolate'
'('
interpolate_type_name ','
? ')'
| '@'
'interpolate'
'('
interpolate_type_name ','
interpolate_sampling_name ','
? ')'
설명 | 사용자 정의 IO가 어떻게 반드시 보간되어야 하는지 지정합니다. 이 속성은 사용자 정의 vertex 출력과 fragment 입력에서만 의미가 있습니다. § 13.3.1.4 보간 참고. |
파라미터 |
첫 번째 파라미터는 반드시
보간 타입 이름-토큰이어야 하며 보간 타입이어야 합니다.
두 번째 파라미터가 있으면 반드시 보간 샘플링 이름-토큰이어야 하며, 보간 샘플링이어야 합니다. |
12.10. invariant
'@'
'invariant'
설명 |
vertex 셰이더의 position 내장 출력 값에 적용될 때,
결과 계산이 서로 다른 프로그램과 동일 엔트리 포인트의 여러 호출에서 불변임을 의미합니다.
즉, 두 position 출력이 서로 다른 엔트리 포인트에서 데이터와 제어 흐름이 일치하면, 결과값도 동일함을 보장합니다.
position 내장 입력 값에는 아무런 영향이 없습니다.
참고: 이 속성은 HLSL의 |
파라미터 | 없음 |
12.11. location
'@'
'location'
'('
expression ','
?
')'
설명 |
엔트리 포인트의 사용자 정의 IO의 일부를 지정합니다.
§ 13.3.1.3 입출력 위치 참고.
반드시 엔트리 포인트 함수 매개변수, 엔트리 포인트 반환 타입, 또는 구조체 타입의 멤버에만 적용해야 합니다. 반드시 숫자 스칼라 또는 숫자 벡터 타입 객체 선언에만 적용해야 합니다. 포함되면 안 됨 compute 셰이더 단계 입력에. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다. 반드시 0 이상이어야 합니다. |
12.12. must_use
'@'
'must_use'
설명 |
이 함수에 대한 호출이 표현식으로 반드시 사용되어야 함을
지정합니다.
즉, 이 함수 호출이 함수 호출 문장 전체가 되어서는 안 됩니다.
반드시 function 선언에만 적용되어야 하며, return type이 있어야 합니다. 참고: 많은 함수는 값을 반환하고 부작용이 없습니다.
그러한 함수를 함수 호출 문장의 전부로 호출하는 것은 흔히 프로그래밍 결함입니다.
이런 특성을 가진 내장 함수는 참고: |
파라미터 | 없음 |
12.13. size
'@'
'size'
'('
expression ','
?
')'
설명 |
구조체 멤버에 예약된 바이트 수를 지정합니다.
이 값은 반드시 해당 멤버 타입의 바이트 크기 이상이어야 합니다:
§ 14.4 메모리 레이아웃 참고. 반드시 구조체 타입의 멤버에만 적용해야 하며, 멤버 타입은 creation-fixed footprint를 반드시 가져야 합니다. |
파라미터 | 반드시 const-expression이어야 하며, 해석된 결과가 i32 또는 u32여야 합니다. 반드시 양수여야 합니다. |
12.14. workgroup_size
'@'
'workgroup_size'
'('
expression ','
? ')'
| '@'
'workgroup_size'
'('
expression ','
expression ','
?
')'
| '@'
'workgroup_size'
'('
expression ','
expression ','
expression ','
?
')'
설명 |
컴퓨트 셰이더의 워크그룹
그리드의 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'
vertex
속성은 해당 함수를
버텍스 셰이더 스테이지의 엔트리 포인트로 선언하며,
렌더 파이프라인에 사용됩니다.
12.15.2. fragment
'@'
'fragment'
fragment
속성은 해당 함수를
프래그먼트 셰이더 스테이지의
엔트리 포인트로 선언하며,
렌더 파이프라인에 사용됩니다.
12.15.3. compute
'@'
'compute'
compute
속성은 해당 함수를
컴퓨트 셰이더 스테이지의 엔트리 포인트로 선언하며,
컴퓨트 파이프라인에 사용됩니다.
13. 엔트리 포인트
엔트리 포인트는 특정 셰이더 스테이지의 작업을 수행하는 사용자 정의 함수입니다.
13.1. 셰이더 스테이지
WebGPU는 draw 또는 dispatch 명령의 형태로 GPU에 작업을 전달합니다. 이러한 명령들은 셰이더 스테이지의 입력, 출력 및 연결된 리소스들의 컨텍스트에서 파이프라인을 실행합니다.
파이프라인은 GPU에서 수행할 작업을 정의하며, 일부가 프로그래머블한 여러 단계의 순서로 이루어집니다. WebGPU에서는 실행할 draw 또는 dispatch 명령을 예약하기 전에 파이프라인을 생성합니다. 파이프라인에는 두 가지 종류가 있습니다: GPUComputePipeline과 GPURenderPipeline.
dispatch 명령은 GPUComputePipeline을 사용하여 컴퓨트 셰이더 스테이지를 논리적 포인트 그리드에 대해 실행하며, 병렬 처리의 양을 제어할 수 있고, 버퍼 및 이미지 리소스를 읽거나 갱신할 수 있습니다.
draw 명령은 GPURenderPipeline을 사용하여 두 개의 프로그래머블 스테이지와 기타 고정 기능 스테이지로 이루어진 다단계 프로세스를 실행합니다:
-
버텍스 셰이더 스테이지는 단일 버텍스의 입력 속성을 해당 버텍스의 출력 속성으로 변환합니다.
-
고정 기능 스테이지는 버텍스를 그래픽 프리미티브(예: 삼각형)로 변환하고, 이후 래스터화하여 프래그먼트를 생성합니다.
-
프래그먼트 셰이더 스테이지는 각 프래그먼트를 처리하며, 프래그먼트 출력을 생성할 수 있습니다.
-
고정 기능 스테이지는 프래그먼트 출력을 소비하여 색상 첨부 및 깊이/스텐실 버퍼 등 외부 상태를 갱신할 수 있습니다.
WebGPU 명세는 파이프라인에 대해 더 자세하게 설명합니다.
WGSL은 파이프라인의 프로그래머블 부분에 해당하는 세 가지 셰이더 스테이지를 정의합니다:
-
compute
-
vertex
-
fragment
각 셰이더 스테이지는 고유의 특성과 제약을 가지며, 자세한 내용은 다른 부분에서 설명합니다.
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은 엔트리 포인트 선언에 적용할 수 있는 다음 속성들을 정의합니다:
@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는 셰이더에 의해 정적으로 접근됨으로 간주될 때:
-
어떤 셰이더 단계의 함수 선언에서 D로 해결되는 식별자가 등장할 때.
이제 셰이더의 인터페이스를 다음으로 정확히 정의할 수 있다:
-
엔트리 포인트의 반환값. 이는 셰이더 단계 출력을 나타낸다.
13.3.1. 단계 간 입력 및 출력 인터페이스
셰이더 단계 입력은 파이프라인 상류에서 셰이더 단계에 제공되는 데이터다. 각 데이터는 내장 입력 값 또는 사용자 정의 입력 중 하나다.
셰이더 단계 출력은 셰이더가 파이프라인 하류의 추가 처리에 제공하는 데이터다. 각 데이터는 내장 출력 값 또는 사용자 정의 출력 중 하나다.
IO 어트리뷰트는 객체를 셰이더 단계 입력 또는 셰이더 단계 출력으로 지정하거나, 입력 또는 출력의 특성을 추가로 설명하는 데 사용된다. IO 어트리뷰트 종류는 다음과 같다:
13.3.1.1. 내장 입력 및 출력
내장 입력 값은 시스템에서 생성된 제어 정보를 접근하기 위한 것이다. 엔트리 포인트 반드시 중복된 내장 입력을 포함하지 않아야 한다.
단계 S의 이름 X 및 타입 TX을 가진 내장 입력은 셰이더 단계 S에 대한 엔트리 포인트의 형식 매개변수를 통해 접근되며, 두 가지 방식이 있다:
-
매개변수에
builtin(
X)
어트리뷰트가 있고 타입이 TX이다. -
매개변수에 구조체 타입이 선언되어 있고, 구조체 멤버 중 하나가
builtin(
X)
어트리뷰트와 TX 타입을 가진다.
반대로 엔트리 포인트의 매개변수 또는 매개변수 멤버에 builtin 어트리뷰트가 있으면, 해당 builtin 반드시 해당 셰이더 단계의 입력이어야 한다.
내장 출력 값은 셰이더가 파이프라인의 이후 처리 단계에 제어 정보를 전달하는 데 사용된다. 엔트리 포인트 반드시 중복된 내장 출력을 포함하지 않아야 한다.
단계 S에 대해 이름 Y 및 타입 TY을 가진 내장 출력은 셰이더 단계 S의 엔트리 포인트의 반환값을 통해 다음 두 가지 방식으로 설정된다:
-
엔트리 포인트 반환 타입에
builtin(
Y)
어트리뷰트가 있고 타입이 TY이다. -
엔트리 포인트 반환 타입이 구조체 타입이고, 구조체 멤버 중 하나가
builtin(
Y)
어트리뷰트와 TY 타입을 가진다.
반대로 엔트리 포인트의 반환 타입 또는 반환 타입의 멤버에 builtin 어트리뷰트가 있으면, 해당 builtin 반드시 해당 셰이더 단계의 출력이어야 한다.
참고: position 내장은 버텍스 셰이더의 출력이자 프래그먼트 셰이더의 입력이다.
내장 입력 값과 내장 출력 값을 통틀어 내장 값이라 한다.
아래 표는 사용 가능한 내장 값을 요약한다. 각각은 내장 값 이름 토큰이며, 내장 값에 해당한다. 각각은 이후 섹션에서 자세히 설명된다.
이름 | 단계 | 방향 | 타입 | 확장 |
---|---|---|---|---|
vertex_index | 버텍스 | 입력 | u32 | |
instance_index | 버텍스 | 입력 | u32 | |
clip_distances | 버텍스 | 출력 | array<f32, N> (N ≤ 8 )
| 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의 |
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를 프래그먼트의 입력 위치라고 합시다. 그러면 개략적으로: fp.xy = rp.destination.position 자세히 보면:
자세한 내용은 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 레벨 드로우 명령에서, 인스턴싱과 관계없이 현재 꼭짓점의 인덱스입니다.
비인덱스 드로우의 경우, 첫 꼭짓점 인덱스는 드로우의 인덱스 드로우의 경우, 인덱스는 꼭짓점의 인덱스 버퍼 값에 드로우의 |
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)는 반드시 다음을 만족해야 합니다:
-
숫자 스칼라(numeric scalar) 타입 또는 숫자 벡터(numeric vector) 타입이어야 합니다.
-
IO location이 할당되어야 합니다. 자세한 내용은 § 13.3.1.3 입력-출력 위치(Input-output Locations)를 참고하세요.
compute 셰이더는 사용자 정의 입력 또는 출력을 가질 수 없습니다.
13.3.1.3. 입출력 위치
각 입출력 위치는 최대 16바이트 크기의 값을 저장할 수 있습니다. 타입의 바이트 크기는 § 14.4.1 정렬과 크기의 SizeOf 열을 사용하여 정의됩니다. 예를 들어, 네 개의 부동소수점 성분을 가진 벡터는 하나의 위치를 차지합니다.
IO 위치는 location 어트리뷰트를 통해 지정됩니다.
각 사용자 정의 입력 및 출력 반드시 명시적으로 지정된 IO 위치를 가져야 합니다. 엔트리 포인트 IO의 각 구조체 멤버는 반드시 내장 값(자세한 내용은 § 13.3.1.1 내장 입력 및 출력 참고) 중 하나이거나, 위치가 할당되어야 합니다.
-
inputs에는 동일한 location 값을 가진 두 항목이 포함되면 안 됩니다.
-
만약 members의 어떤 항목이 blend_src 어트리뷰트를 지정한다면, members는 듀얼 소스 블렌딩을 사용해야 합니다, 즉:
-
그 외의 경우, members에는 동일한 location 값을 가진 두 항목이 포함되면 안 됩니다.
참고: 위치 번호는 입력과 출력에서 구분됩니다: 엔트리 포인트 셰이더 단계의 입력에 대한 위치 번호는 엔트리 포인트 셰이더 단계의 출력에 대한 위치 번호와 충돌하지 않습니다.
참고: 엔트리 포인트의 출력에서 위치가 겹치는 것을 방지하기 위한 추가 규칙은 필요하지 않습니다. 출력이 구조체일 경우 위 첫 번째 규칙이 겹침을 방지합니다. 그렇지 않으면 출력은 스칼라 또는 벡터이고, 하나의 위치만 할당될 수 있습니다.
참고: 엔트리 포인트에서 사용할 수 있는 위치의 수는 WebGPU API에 의해 정의됩니다.
사용자 정의 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 { // ... }
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의 경우:
-
보간 어트리뷰트가 지정되지 않으면
@interpolate(perspective, center)
가 기본값으로 적용됩니다. -
보간 어트리뷰트가 보간 타입과 함께 지정되면:
스칼라 또는 벡터 정수 타입의 사용자 정의 vertex 출력 및 fragment 입력은 반드시 항상 flat
보간 타입으로
지정되어야 합니다.
단계 간 인터페이스 검증에서는 렌더 파이프라인 내에서 각 사용자 정의 fragment 입력의 보간 속성이 동일한 location 할당을 가진 vertex 출력의 보간 속성과 일치하는지 확인합니다. 그렇지 않으면 파이프라인 생성 오류가 발생합니다.
13.3.2. 리소스 인터페이스
리소스(resource)는 셰이더 단계 외부의 데이터에 접근할 수 있도록 하는 객체로, override 선언이나 셰이더 단계 입력/출력이 아닙니다. 리소스는 셰이더의 모든 호출(invocation)에서 공유됩니다.
리소스에는 네 가지 종류가 있습니다:
셰이더의 리소스 인터페이스(resource interface of a shader)는 셰이더 단계의 함수에서 정적으로 접근되는 모듈 범위의 리소스 변수 집합입니다.
각 리소스 변수는 반드시 group 및 binding 어트리뷰트와 함께 선언되어야 합니다. 셰이더의 단계와 함께 이 값들은 셰이더 파이프라인에서 리소스의 바인딩 주소를 식별합니다. 자세한 내용은 WebGPU § 8.3 GPUPipelineLayout을 참고하세요.
같은 셰이더 내의 두 개의 서로 다른 리소스 변수는 반드시 group 및 binding 값이 쌍으로 같아서는 안 됩니다.
13.3.3. 리소스 레이아웃 호환성
WebGPU는 셰이더의 리소스 인터페이스가 셰이더를 사용하는 파이프라인의 레이아웃과 일치해야 합니다.
WGSL 변수의 리소스 인터페이스가 WebGPU binding member 또는 binding type과 호환되지 않는 경우, 이는 파이프라인 생성 오류입니다. 호환성은 아래 표에서 정의됩니다.
인터페이스 검증 요구사항은 WebGPU API 명세를 참고하세요.
13.3.4. 버퍼 바인딩이 런타임 크기 배열의 요소 개수를 결정함
storage buffer
변수에 런타임 크기 배열이 포함되어 있으면, 해당
배열의 요소 개수는 해당 resource
의
크기로 결정됩니다.
T를 store type이라 하며, 이는 storage buffer 변수의 타입입니다. T는 런타임 크기 배열 타입이거나 해당 타입을 포함합니다.
bufferBinding은 get as buffer binding(
resource
)입니다.EBS는 effective buffer binding size로, 해당 storage buffer 변수에 대응하는 파이프라인 바인딩 주소에 바인딩된 bufferBinding에 대해 계산됩니다.
그렇다면 NRuntime, 즉 런타임 크기 배열의 요소 개수는 SizeOf(T) ≤ EBS를 만족하는 가장 큰 정수입니다.
자세히 설명하면, NRuntime은 타입 RAT의 런타임 크기 배열에 대해 다음과 같이 계산됩니다:
truncate((EBBS − array_offset) ÷ array_stride), 여기서:
EBBS는 변수에 연결된 effective buffer binding size입니다.
array_offset은 변수의 store type 내에서 런타임 크기 배열의 바이트 오프셋입니다.
store type이 RAT일 경우, 즉 런타임 크기 배열 타입 자체라면 0입니다.
그 외에 store type이 구조체이고, 그 마지막 멤버가 런타임 크기 배열인 경우, array_offset은 해당 멤버의 바이트 오프셋입니다.
array_stride는 배열 타입의 stride, 즉 StrideOf(RAT)입니다.
셰이더는 NRuntime을 arrayLength 내장 함수로 계산할 수 있습니다.
NRuntime은 해당 버퍼 바인딩의 크기에 따라 결정되며, draw 또는 dispatch command마다 달라질 수 있습니다.
WebGPU 검증 규칙은 1 ≤ NRuntime임을 보장합니다.
-
weights
변수는 storage buffer입니다. -
store type은 런타임 크기 배열 타입
array<f32>
입니다. -
배열 오프셋은 0입니다.
-
배열 stride는 StrideOf(array<f32>), 즉 4입니다.
아래 표는 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 ) |
-
lights
변수는 storage buffer입니다. -
store type은
LightStorage
입니다. -
LightStorage
의point
멤버는array<PointLight>
타입의 런타임 크기 배열입니다.
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)이란 메모리 위치에 작용하는 연산을 말합니다.
-
읽기 접근(read access)은 메모리 위치의 내용을 관찰합니다.
-
쓰기 접근(write 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 type이 storage texture이고
쓰기(write) 또는 읽기/쓰기(read_write) 접근 모드인 변수는
버텍스 셰이더 스테이지에서 정적으로 접근할 수 없습니다.
WebGPU createBindGroupLayout()
를
참조하세요.
참고: 각 주소 공간은 성능 특성이 다를 수 있습니다.
WGSL 소스에서 변수 선언이나 포인터 타입을 작성할 때:
-
다른 주소 공간에서는 접근 모드를 작성하면 안 됩니다.
14.4. 메모리 레이아웃
WGSL에서 타입의 레이아웃은 주소 공간과는 독립적입니다. 하지만 엄밀히 말하면, 해당 레이아웃은 호스트-공유(host-shareable) 버퍼에서만 관찰될 수 있습니다. Uniform buffer와 storage buffer 변수는 메모리 내 바이트 시퀀스로 구성된 대량 데이터를 공유하는 데 사용됩니다. 버퍼는 CPU와 GPU 사이, 또는 서로 다른 셰이더 스테이지 간, 또는 다른 파이프라인 간에 공유될 수 있습니다.
버퍼 데이터는 재포맷이나 변환 없이 공유되므로, 버퍼 생성자와 소비자가 메모리 레이아웃(버퍼 내 바이트가 WGSL 타입 값으로 어떻게 조직되는지에 대한 설명)에 동의하지 않는다면 동적 오류가 발생합니다. 이러한 바이트는 공통 기준 위치(base location)에 상대적인 값의 메모리 위치입니다.
버퍼 변수의 store type은 반드시 호스트-공유 타입이어야 하며, 아래에 설명된 대로 완전히 명시된 메모리 레이아웃을 가져야 합니다.
각 버퍼 변수는 반드시 uniform 또는 storage 주소 공간에 선언되어야 합니다.
타입의 메모리 레이아웃은 다음과 같은 경우에만 의미가 있습니다:
8비트 바이트는 호스트-공유 메모리의 가장 기본 단위입니다. 본 섹션에서 정의된 용어들은 8비트 바이트 수를 표현합니다.
다음과 같은 표기법을 사용합니다. T는 호스트-공유 또는 고정 풋프린트(fixed footprint) 타입, S는 호스트-공유 또는 고정 풋프린트 구조체 타입, A는 호스트-공유 또는 고정 풋프린트 배열 혹은 런타임 크기 배열입니다:
-
AlignOf(T): T의 정렬값(alignment) 입니다.
-
AlignOfMember(S, i): S의 i번째 멤버의 정렬값입니다.
-
SizeOf(T): T의 바이트 크기(byte-size) 입니다.
-
SizeOfMember(S, i): S의 i번째 멤버의 크기입니다.
-
OffsetOfMember(S, i): S의 i번째 멤버가 S의 시작점으로부터 떨어진 바이트 오프셋입니다.
-
StrideOf(A): A의 요소 stride입니다. 즉, 한 배열 요소의 시작점에서 다음 요소의 시작점까지 바이트 수입니다. 이는 배열 요소 타입의 크기를 해당 요소 타입의 정렬값으로 올림(round up)한 값과 같습니다:
StrideOf(array<E, N>) = roundUp(AlignOf(E), SizeOf(E))
StrideOf(array<E>) = roundUp(AlignOf(E), SizeOf(E))
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>, T가 bool, i32, u32, 또는 f32일 때 | 8 | 8 |
vec2<f16> | 4 | 4 |
vec3<T>, T가 bool, i32, u32, 또는 f32일 때 | 16 | 12 |
vec3<f16> | 8 | 6 |
vec4<T>, T가 bool, 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의 런타임 결정 요소 개수입니다. |
bool
값이 4바이트 크기와 4바이트 정렬값을 갖도록 명시함으로써,
구현체는 인접한 boolean 값들을 데이터 레이스 없이 메모리에 저장할 수 있습니다.
14.4.2. 구조체 멤버 레이아웃
구조체의 내부 레이아웃은 멤버들의 크기와 정렬값으로 계산됩니다. 기본적으로, 멤버들은 겹침 없이, 순서대로, 멤버 정렬 요구사항을 만족하면서 촘촘하게 배치됩니다.
이 기본 내부 레이아웃은 레이아웃 속성(layout attributes)에 의해 오버라이드할 수 있습니다. 레이아웃 속성은 다음과 같습니다:
구조체 타입 S의 i번째 멤버는 SizeOfMember(S, i)와 AlignOfMember(S, i)로 표시되는 크기와 정렬값을 가집니다. 멤버 크기와 정렬값은 각 멤버가 구조체 시작점으로부터 떨어진 바이트 오프셋을 계산하는 데 사용됩니다. 자세한 내용은 § 14.4.4 값의 내부 레이아웃에 나와 있습니다.
SizeOfMember(S, i)는 S의 i번째 멤버에 size(k) 속성이 있으면 k입니다. 그렇지 않으면 해당 멤버의 타입 T에 대해 SizeOf(T)입니다.
AlignOfMember(S, i)는 S의 i번째 멤버에 align(k) 속성이 있으면 k입니다. 그렇지 않으면 해당 멤버 타입 T에 대해 AlignOf(T)입니다.
구조체 멤버에 size 속성이 지정된 경우, 값은 해당 멤버 타입의 크기 이상이어야 합니다:
SizeOfMember(S, i) ≥ SizeOf(T)
여기서 T는 S의 i번째 멤버의 타입입니다.
첫 번째 구조체 멤버는 항상 구조체 시작점에서 바이트 오프셋 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. 값의 내부 레이아웃
이 섹션은 호스트-공유 값의 내부가 버퍼의 바이트 위치에 어떻게 배치되는지 설명합니다. 전체 값의 배치가 주어졌을 때, 이러한 레이아웃은 값의 타입과 구조체 멤버의 align 및 size 속성에 따라 달라집니다.
값이 배치되는 버퍼 바이트 오프셋은 타입의 정렬 요구조건을 만족해야 합니다: 타입 T의 값이 버퍼 오프셋 k에 배치된다면, k = c × AlignOf(T) (단, c는 0 이상의 정수)이어야 합니다.
데이터는 주소 공간에 상관없이 동일하게 나타납니다.
참고: bool 타입은 호스트-공유가 아닙니다. WGSL은 bool 값이 4바이트 크기와 정렬값을 갖도록 명시하지만, 내부 레이아웃은 명시하지 않습니다.
u32 또는 i32 타입의 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:
-
바이트 k: V의 0~7번 비트
-
바이트 k+1: V의 8~15번 비트
-
바이트 k+2: V의 16~23번 비트
-
바이트 k+3: V의 24~31번 비트
참고: i32는 2의 보수 표현을 사용하므로 부호 비트는 31번 위치에 있습니다.
64비트 정수 레이아웃: WebGPU API의 일부 기능은 버퍼에 64비트 부호 없는 정수 값을 기록합니다. 이러한 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:
-
바이트 k: V의 0~7번 비트
-
바이트 k+1: V의 8~15번 비트
-
바이트 k+2: V의 16~23번 비트
-
바이트 k+3: V의 24~31번 비트
-
바이트 k+4: V의 32~39번 비트
-
바이트 k+5: V의 40~47번 비트
-
바이트 k+6: V의 48~55번 비트
-
바이트 k+7: V의 56~63번 비트
참고: WGSL에는 구체적인(concrete) 64비트 정수 타입이 없습니다.
f32 타입 값 V는 IEEE-754 binary32 포맷으로 표현됩니다. 1비트 부호, 8비트 지수, 23비트 분수(fraction)로 구성됩니다. V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:
-
바이트 k: 분수의 0~7번 비트
-
바이트 k+1: 분수의 8~15번 비트
-
바이트 k+2의 0~6번 비트: 분수의 16~22번 비트
-
바이트 k+2의 7번 비트: 지수의 0번 비트
-
바이트 k+3의 0~6번 비트: 지수의 1~7번 비트
-
바이트 k+3의 7번 비트: 부호 비트
f16 타입 값 V는 IEEE-754 binary16 포맷으로 표현됩니다. 1비트 부호, 5비트 지수, 10비트 분수로 구성됩니다. V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치된다면:
-
바이트 k: 분수의 0~7번 비트
-
바이트 k+1의 0~1번 비트: 분수의 8~9번 비트
-
바이트 k+1의 2~6번 비트: 지수의 0~4번 비트
-
바이트 k+1의 7번 비트: 부호 비트
참고: 위 규칙은 숫자 값이 호스트-공유 버퍼에 리틀 엔디안(little-endian) 형식으로 저장됨을 의미합니다.
atomic 타입
atomic
<T> 값 V가 호스트-공유 버퍼에 배치될 때, 내부 레이아웃은 기반 타입 T 값과 동일합니다.
벡터 타입 vecN<T> 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치될 때:
-
V.x는 바이트 오프셋 k에 배치
-
V.y는 바이트 오프셋 k + SizeOf(T)에 배치
-
N ≥ 3이면 V.z는 k + 2 × SizeOf(T)에 배치
-
N ≥ 4이면 V.w는 k + 3 × SizeOf(T)에 배치
행렬 타입 matCxR<T> 값 V가 호스트-공유 버퍼의 바이트 오프셋 k에 배치될 때:
-
V의 i번째 열 벡터는 바이트 오프셋 k + i × AlignOf(vecR<T> )에 배치됨
배열 타입 A 값이 호스트-공유 메모리 버퍼의 바이트 오프셋 k에 배치될 때:
-
배열의 i번째 요소는 바이트 오프셋 k + i × StrideOf(A)에 배치됨
구조체 타입 S의 값이 호스트-공유 메모리 버퍼의 바이트 오프셋 k에 배치될 때:
-
구조체 값의 i번째 멤버는 바이트 오프셋 k + OffsetOfMember(S,i)에 배치됩니다. 자세한 내용은 § 14.4.2 구조체 멤버 레이아웃을 참조하세요.
14.4.5. 주소 공간 레이아웃 제약
storage와 uniform 주소 공간은 서로 다른 버퍼 레이아웃 제약을 가지며, 이 절에서 설명합니다.
모든 주소 공간(address space)은 uniform을 제외하고 storage 주소 공간과 동일한 제약을 갖습니다.
변수가 직접 또는 간접적으로 참조하는 모든 구조체 및 배열 타입은 해당 변수의 주소 공간의 제약을 반드시 따라야 합니다. 주소 공간 제약을 위반하면 셰이더 생성 오류가 발생합니다.
이 절에서는 RequiredAlignOf(S, C)를 주소 공간 C에서 호스트-공유 또는 고정 풋프린트 타입 S 값의 바이트 오프셋 정렬값 요구사항으로 정의합니다.
주소 공간 C에 나타날 수 있다고 가정한 호스트-공유 또는 고정 풋프린트 타입 S | RequiredAlignOf(S, C), C가 uniform이 아닌 경우 | RequiredAlignOf(S, C), C가 uniform인 경우 |
---|---|---|
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 이상의 정수이며, 구조체 S의 i번째 멤버의 타입이 T임
주소 공간 C에서 요소 타입 T 배열은 RequiredAlignOf(T, C)의 배수인 요소 stride를 반드시 가져야 합니다:
StrideOf(array<T, N>) = k × RequiredAlignOf(T, C)
StrideOf(array<T>) = k × RequiredAlignOf(T, C)
여기서 k는 양의 정수임
uniform 주소 공간은 다음도 요구합니다:
-
배열 요소는 16바이트 경계에 정렬되어야 합니다. 즉, StrideOf(array<T,N>) = 16 × k' (단, k'는 양의 정수)
-
구조체 멤버가 구조체 타입
S
인 경우, 해당 멤버의 시작점에서 다음 멤버의 시작점까지 바이트 수는 roundUp(16, SizeOf(S)) 이상이어야 합니다.
참고: 아래 예시는 구조체 멤버에 align 및 size 속성을 사용하여 uniform 버퍼의 레이아웃 요구사항을 만족시키는 방법을 보여줍니다. 특히 이러한 기법은 std140 레이아웃의 GLSL 버퍼를 WGSL로 기계적으로 변환할 때 사용할 수 있습니다.
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 ;
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 메모리 모델의 메모리 쓰기 연산과 동일합니다.
읽기 접근은 호출이 다음 중 하나를 실행할 때 발생합니다:
-
Load Rule 평가
-
아래 함수를 제외한 모든 texture 내장 함수:
-
atomicStore를 제외한 모든 atomic 내장 함수
-
workgroupUniformLoad 내장 함수
-
복합 대입문 (좌변 식에 대해)
쓰기 접근은 호출이 다음 중 하나를 실행할 때 발생합니다:
-
textureStore 내장 함수
-
atomicLoad를 제외한 모든 atomic 내장 함수
-
atomicCompareExchangeWeak는 반환 결과의
exchanged
멤버가true
일 때만 write 수행
-
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 변수는 고유한 group과 binding 쌍에 대한 메모리 모델 참조(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의 저장소 클래스와 동일합니다.
workgroupBarrier는 AcquireRelease
메모리 의미론과 WorkgroupMemory
의미론을 사용합니다.
storageBarrier는 AcquireRelease
메모리 의미론과 UniformMemory
의미론을 사용합니다.
textureBarrier는 AcquireRelease
메모리 의미론과 ImageMemory
의미론을 사용합니다.
참고: workgroupBarrier
와
storageBarrier
를 함께 사용하면 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의 선택자 값을 다르게 계산하거나, 단락 평가 이항 연산자(
&&
또는||
)의 좌측 피연산자가 다를 때 비균일 제어 의존성이 발생합니다.
이러한 비균일 값은 종종 정적으로 균일하다고 증명되지 않은 특정 소스에서 유래할 수 있습니다. 대표적인 소스는 다음과 같습니다:
-
대부분의 내장 값 (단, num_workgroups와 workgroup_id는 예외)
-
특정 내장 함수 (자세한 내용은 § 15.2.7 함수 호출의 균일성 규칙 참조)
올바르고 이식 가능한 동작을 보장하기 위해, WGSL 구현은 정적 균일성 분석(uniformity analysis)을 수행하여 각 집합 연산이 균일 제어 흐름에서 실행된다는 것을 증명하려고 시도합니다. 이후의 하위 섹션에서 분석을 설명합니다.
균일성 실패(uniformity failure)는 발생하며, 균일성 분석이 특정 집합 연산이 균일 제어 흐름에서 실행된다는 것을 증명하지 못할 때 발생합니다.
-
도함수를 계산하는 내장 함수에서 균일성 실패가 발생하면 derivative_uniformity 진단(diagnostic)이 트리거됩니다.
-
동기화 내장 함수(synchronization builtin)에서 균일성 실패가 발생하면, 오류 진단이 트리거되어, 셰이더 생성 오류가 발생합니다.
-
subgroup 또는 quad 내장 함수에서 균일성 실패가 발생하면, subgroup_uniformity 진단이 트리거됩니다.
-
진단의 트리거 위치는 해당 내장 함수의 호출 위치이거나, subgroupShuffleUp, subgroupShuffleDown, subgroupShuffleXor의 경우에는 균일해야 하는 파라미터의 위치입니다.
-
15.2.1. 용어 및 개념
다음 정의는 단순히 직관을 제공하기 위한 것으로, 이후 하위 섹션에서 실제 분석이 계산하는 개념입니다. 분석이 실제로 이러한 개념과 프로그램의 유효성, 균일성 규칙 위반 여부를 정의합니다.
주어진 호출 집합에 대해:
-
주어진 스코프의 모든 호출이 프로그램의 특정 지점에서 lockstep으로 실행되는 것처럼 동작하면, 그 지점은 균일 제어 흐름(uniform control flow)을 가진다고 합니다.
-
컴퓨트 셰이더 스테이지에서는 균일 제어 흐름의 스코프는 동일 워크그룹(workgroup) 내 모든 호출입니다.
-
다른 셰이더 스테이지에서는 균일 제어 흐름의 스코프는 동일 엔트리 포인트(entry point)와 동일 draw 명령의 모든 호출입니다.
-
-
표현식이 균일 제어 흐름에서 실행되고, 모든 호출이 동일 값을 계산하면, 해당 값은 균일 값(uniform value)이라 합니다.
-
호출들이 로컬 변수에 대해 그 변수가 생존하는 모든 지점에서 동일 값을 유지하면, 해당 변수는 균일 변수(uniform variable)라 합니다.
15.2.2. 균일성 분석 개요
이후 하위 섹션에서는 집합 연산이 균일 제어 흐름에서만 실행되는지 확인하기 위한 정적 분석을 지정합니다.
분석은 동적 오류(dynamic error)가 발생하지 않는다고 가정합니다. 동적 오류가 발생한 셰이더 스테이지는 균일성 분석 결과와 관계 없이 이미 이식성이 없습니다.
-
타당성(sound): 균일성 실패(uniformity failure)가 발생하여, 내장 함수의 균일성 요구사항을 위반하는 프로그램을 검출할 수 있습니다.
-
코드를 함수로 리팩터링하거나, 함수를 인라인하는 것은 셰이더가 이전에 유효했다면 유효성을 깨지 않습니다.
-
분석이 프로그램을 거부하면, 사용자 에이전트가 좋은 오류 메시지를 작성할 수 있도록 명확한 암시(chain of implications)를 제공합니다.
각 함수는 두 가지를 보장하도록 분석됩니다:
-
다른 함수를 호출할 때 균일성 요구사항이 만족되는지,
-
자신이 호출될 때마다 균일성 요구사항이 만족되는지.
분석의 일환으로, 각 함수 호출에 대해 해당 호출이 균일 제어 흐름에서 실행된다고 증명할 수 없는 경우 트리거될 트리거 규칙(triggering rule) 집합을 계산하고 전파합니다. 이를 호출의 잠재적 트리거 집합(potential-trigger-set)이라고 합니다. 이 집합의 요소는 다음에서 선택됩니다:
-
도함수 계산에 의존하는 함수에 대한 derivative_uniformity
-
§ 17.12 Subgroup 내장 함수 또는 § 17.13 Quad 연산의 함수에 대한 subgroup_uniformity, 또는
-
필터링할 수 없는 균일성 요구사항에 대한 이름 없는 트리거 규칙.
-
이는 동기화 함수에 의존하는 컴퓨트 셰이더 함수에 사용됩니다.
-
15.2.3. 함수의 균일성 요구사항 분석
각 함수는 두 단계로 분석됩니다.
첫 번째 단계에서는 함수의 구문(syntax)을 따라가면서, 다음 하위 섹션의 규칙에 따라 방향성 그래프(directed graph)를 생성합니다. 두 번째 단계에서는 이 그래프를 탐색하여, 이 함수 호출에 대한 제약 조건을 계산하고, 균일성 실패(uniformity failure)가 발생할 수 있습니다.
-
프로그램의 특정 지점은 균일 제어 흐름에서 실행되어야 한다.
-
표현식은 균일 값(uniform value)이어야 한다.
-
변수는 균일 변수(uniform variable)이어야 한다.
-
메모리에 저장된 값(포인터를 통해 로드될 수 있는 값)은 균일 값이어야 한다.
엣지(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.warning과 RequiredToBeUniform.info 노드도 비슷하게 사용되지만, 경고(warning)나 정보(info) 진단(diagnostic)이 언제 트리거될지 판단하는 데 사용됩니다:
-
RequiredToBeUniform.warning에서 MayBeNonUniform까지 경로가 있으면, 경고(warning) 진단이 트리거됩니다.
-
RequiredToBeUniform.info에서 MayBeNonUniform까지 경로가 있으면, 정보(info) 진단이 트리거됩니다.
§ 2.3 진단(Diagnostics)에 설명된 바와 같이, 높은 심각도의 진단이 생성된 경우 낮은 심각도의 진단은 무시될 수 있습니다.
각 함수에 대해 두 개의 태그(tag)가 계산됩니다:
-
함수의 호출 지점(call site)에서 제어 흐름의 균일성 요구사항을 설명하는 호출 지점 태그(call site tag)
-
함수가 균일성에 미치는 영향을 설명하는 함수 태그(function tag)
각 함수의 형식 파라미터(formal parameter)마다 하나 또는 두 개의 태그가 계산됩니다:
-
매개변수 태그(parameter tag)는 매개변수 값의 균일성 요구사항을 설명합니다.
-
매개변수 반환 태그(parameter return tag)는 매개변수의 균일성이 함수의 반환값의 균일성에 어떤 영향을 미치는지를 설명합니다.
-
포인터 매개변수 태그(pointer parameter tag)는 매개변수 타입이 function 주소 공간의 포인터일 때 사용합니다. 이 태그는 함수 호출 실행 중에 매개변수가 가리키는 메모리의 값이 비균일(non-uniform)이 될 수 있는지를 설명합니다.
Call Site Tag | 설명 |
---|---|
CallSiteRequiredToBeUniform.S, 여기서 S는 error, warning, info 중 하나입니다. |
해당 함수는 반드시 균일 제어 흐름(uniform control flow)에서만 호출되어야 합니다.
그렇지 않으면 S 심각도의 진단이 트리거됩니다.
잠재적 트리거 집합(potential-trigger-set)과 연결되어 있습니다. |
CallSiteNoRestriction | 함수는 비균일 제어 흐름(non-uniform control flow)에서 호출될 수 있습니다. |
Function Tag | 설명 |
---|---|
ReturnValueMayBeNonUniform | 함수의 반환 값이 비균일(non-uniform)할 수 있습니다. |
NoRestriction | 함수는 비균일성을 유발하지 않습니다. |
Parameter Tag | 설명 |
---|---|
ParameterRequiredToBeUniform.S, 여기서 S는 error, warning, info 중 하나입니다. |
해당 파라미터는 반드시 균일
값(uniform value)이어야 합니다.
파라미터 타입이 포인터인 경우, 메모리 뷰 자체는 균일해야 하지만
반드시 그 내용까지 균일할 필요는 없습니다.
그렇지 않으면 S 심각도의 진단이 트리거됩니다.
잠재적 트리거 집합(potential-trigger-set)과 연결되어 있습니다. |
ParameterContentsRequiredToBeUniform.S, 여기서 S는 error, warning, info 중 하나입니다. |
포인터 파라미터가 가리키는 메모리에 저장된 값은 반드시 균일 값이어야 합니다.
그렇지 않으면 S 심각도의 진단이 트리거됩니다.
잠재적 트리거 집합(potential-trigger-set)과 연결되어 있습니다. |
ParameterNoRestriction | 파라미터 값에 대한 균일성 요구사항이 없습니다. |
Parameter Return Tag | 설명 |
---|---|
ParameterReturnContentsRequiredToBeUniform | 파라미터가 반드시 균일 값이어야 함수의 반환 값이 균일 값이 됩니다. 포인터 파라미터인 경우, 그 포인터가 가리키는 메모리의 값도 균일해야 합니다. |
ParameterReturnNoRestriction | 파라미터 값에 대한 균일성 요구사항이 없습니다. |
Pointer Parameter Tag | 설명 |
---|---|
PointerParameterMayBeNonUniform | 포인터 파라미터가 가리키는 메모리의 값이 함수 호출 후 비균일(non-uniform)해질 수 있습니다. |
PointerParameterNoRestriction | 포인터 파라미터가 가리키는 메모리 값의 균일성은 함수 호출에 의해 영향을 받지 않습니다. |
다음 알고리즘은 주어진 함수에 대해 이러한 태그를 계산하는 방법을 설명합니다:
-
다음 노드를 생성합니다:
-
RequiredToBeUniform.error, RequiredToBeUniform.warning, RequiredToBeUniform.info. 이들 노드를 통칭하여 RequiredToBeUniform.S 노드라 합니다.
-
각 노드는 잠재적 트리거 집합(potential-trigger-set)과 연결되어 있으며, 초기에는 비어 있습니다.
-
-
MayBeNonUniform
-
CF_start: 함수가 시작될 때의 제어 흐름 균일성 요구조건을 나타냅니다.
-
param_i: i는 함수의 형식 파라미터(formal parameter) 인덱스를 나타냅니다.
-
함수에 반환 타입(return type)이 있으면, Value_return 노드를 생성합니다.
-
-
§ 15.2.4 포인터 디슈가링(pointer desugaring)에 따라 포인터를 변환합니다.
-
형식 파라미터 중 function 주소 공간의 포인터인 경우, 다음 노드를 추가로 생성합니다:
-
param_i_contents: 메모리 뷰의 내용 균일성을 나타냅니다.
-
Value_return_i_contents: 함수가 해당 메모리 뷰의 내용 균일성에 미치는 영향을 나타냅니다.
-
-
-
함수 구문을 따라가며, 다음 섹션(§ 15.2.5 함수 범위 변수 값 분석, § 15.2.6 문장 균일성 규칙, § 15.2.7 함수 호출 균일성 규칙, § 15.2.8 표현식 균일성 규칙)의 규칙에 따라 노드와 엣지를 그래프에 추가합니다. 함수 본문의 시작 제어 흐름은 CF_start를 사용합니다.
-
이 단계에서 추가된 노드를 내부 노드(interior nodes)라 합니다.
-
-
초기화는 다음과 같이 합니다:
-
함수 태그는 NoRestriction으로 초기화합니다.
-
호출 위치 태그는 CallSiteNoRestriction으로 초기화합니다.
-
각 param_i에 대한 파라미터 태그는 ParameterNoRestriction으로 초기화합니다.
-
각 param_i에 대한 파라미터 반환 태그는 ParameterReturnNoRestriction으로 초기화합니다.
-
각 param_i에 대한 포인터 파라미터 태그가 있으면 PointerParameterNoRestriction으로 초기화합니다.
-
-
다음 순서({error, warning, info})로 각 심각도 S에 대해 다음을 수행합니다:
-
R.S를 RequiredToBeUniform.S에서 도달 가능한 미방문 노드 집합으로 둡니다.
-
R.S의 내부 노드에 방문 표시합니다.
-
PTS는 RequiredToBeUniform.S에 연결된 잠재적 트리거 집합(potential-trigger-set)입니다.
-
R.S에 MayBeNonUniform 노드가 포함되면, 균일성 실패(uniformity failure)를 트리거합니다:
-
각 t ∈ PTS에 대해 S 심각도의 진단을 트리거(Trigger)합니다.
-
-
그렇지 않으면:
-
R.S에 CF_start가 포함되고, 호출 위치 태그가 초기화 이후 갱신되지 않았다면, 호출 위치 태그를 CallSiteRequiredToBeUniform.S로 설정하고, 그 잠재적 트리거 집합을 PTS로 설정합니다.
-
R.S에 포함된 각 param_i에 대해, 해당 파라미터 태그가 초기화 이후 갱신되지 않았다면, 그 태그를 ParameterRequiredToBeUniform.S로 설정하고, 그 잠재적 트리거 집합을 PTS로 설정합니다.
-
R.S에 포함된 각 param_i_contents에 대해, 해당 파라미터 태그가 초기화 이후 갱신되지 않았다면, 그 태그를 ParameterContentsRequiredToBeUniform.S로 설정하고, 그 잠재적 트리거 집합을 PTS로 설정합니다.
-
-
-
모든 내부 노드의 방문 표시를 해제합니다.
-
Value_return 노드가 있으면, VR을 Value_return에서 도달 가능한 노드 집합으로 둡니다.
-
VR에 MayBeNonUniform이 포함되면, 함수 태그를 ReturnValueMayBeNonUniform으로 설정합니다.
-
VR에 포함된 각 param_i에 대해, 해당 파라미터 반환 태그를 ParameterReturnContentsRequiredToBeUniform로 설정합니다.
-
-
각 Value_return_i_contents 노드에 대해, VRi를 Value_return_i_contents에서 도달 가능한 노드 집합이라 한다.
-
VRi에 MayBeNonUniform이 포함되어 있으면, 해당 포인터 파라미터 태그를 PointerParameterMayBeNonUniform으로 설정한다.
-
참고: 이 시점에서 전체 그래프는 파괴될 수 있다. 위에서 설명한 태그만 기억하면 이 함수의 호출자를 분석하는 데 충분하다. 그러나 그래프에는 더 많은 정보를 담고 있어, 더 자세한 진단 메시지를 제공할 수 있다. 예를 들어, 한 함수 내의 값이 균일(uniform)임을 증명할 수 없을 때, 이는 다른 함수에서 균일성 실패(uniformity failure)를 트리거하는 데 기여할 수 있다. 정보성 진단 메시지는 비균일 값뿐만 아니라 진단의 트리거 위치에서의 함수 호출까지 설명할 수 있다.
15.2.4. 포인터 디슈가링
function 주소 공간에 있는 포인터 타입의 각 파라미터는 해당 파라미터를 역참조(dereferencing)한 것과 동등한 초기 값을 갖는 로컬 변수 선언으로 디슈가링됩니다. 즉, function 주소 공간 포인터는 로컬 변수 선언의 별칭으로 간주됩니다. 초기 값 할당은 i번째 파라미터에 대한 param_i_contents로의 엣지를 생성합니다 (V(e)는 param_i_contents임).
let 선언 L이 effective-value-type이 포인터 타입일 경우, 다음과 같이 디슈가링됩니다:
-
초기화 식의 각 부분 표현식 SE를 postorder 깊이 우선 순회로 방문:
-
SE가 타입 검사 중 load rule을 호출하고 root identifier가 변경 가능한 변수이면:
-
L 바로 앞에 SE로 초기화된 새로운 let 선언 LSE를 생성
-
L에서 SE를 LSE로 구성된 값 식별자 표현식으로 교체
-
-
-
업데이트된 L의 초기화 식을 기록
이 디슈가링은 포인터의 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)입니다:
-
변수의 effective-value-type이 스칼라 타입이거나,
-
변수의 effective-value-type이 합성(composite) 타입이고, 합성의 각 컴포넌트가 값이 할당됨.
그 외의 할당은 부분 할당(partial assignment)입니다.
전체 참조(full reference)는 참조 타입의 표현식 중 다음 중 하나입니다:
-
변수로 해결되는 식별자 x
-
(
r)
에서 r은 전체 참조 -
*
p에서 p은 전체 포인터(full pointer)
전체 포인터(full pointer)는 포인터 타입의 표현식 중 다음 중 하나입니다:
참고: 본 분석 목적상, 포인터 타입의 형식 파라미터가 전체 포인터인 경우는 필요하지 않습니다.
전체 참조와 전체 포인터 모두, 해당 원본 변수 x의 모든 메모리 위치에 대한 메모리 뷰입니다.
전체 참조가 아닌 참조는 부분 참조(partial reference)입니다. 부분 참조는 다음 중 하나입니다:
-
해당 원본 변수의 메모리 위치 엄격한 부분집합에 대한 메모리 뷰, 또는
-
원본 변수와 동일한 메모리 위치 집합에 대한 메모리 뷰이지만, 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
에 대해 쓰여집니다. 다음 표기를 사용합니다:
-
Vin(S): 문장 S 실행 이전의
x
값 -
Vout(S): 문장 S 실행 이후의
x
값 -
Vout(prev): 현재 문장 실행 이전의
x
값 -
Vin(next): 다음 문장 실행 이전의
x
값 -
V(e): 이후 섹션의 표현식에 대한 값 노드
-
V(0):
x
의 effective-value-type의 0 값
문장 | 결과 | 결과에서의 엣지 |
---|---|---|
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 여기서 Next는 s1의 동작에 포함됨 참고: s1은 종종 세미콜론으로 끝남. | Vin(s2) | Vout(s1) |
if e s1 else s2 여기서 Next는 s1과 s2의 동작에 모두 포함됨 | Vin(next) | Vout(s1), Vout(s2) |
if e s1 else s2 여기서 Next는 s1의 동작에만 포함됨 | Vin(next) | Vout(s1) |
if e s1 else s2 여기서 Next는 s2의 동작에만 포함됨 | 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는 값 분석의 결과 값으로 사용됩니다. |
for 및 while 반복문의 분석은 각각 loop 문으로 변환된 디슈가 규칙을 따릅니다.
switch에서 default-alone 절 블록은 균일성 측면에서 case 절과 동일하게 취급됩니다.
성능을 극대화하기 위해 구현체는 비균일 제어 흐름을 최소화하려고 시도합니다. 하지만 호출이 균일하다고 볼 수 있는 지점은 여러 요인에 따라 달라집니다. WGSL의 정적 분석은 if, switch, loop 문 끝에서 {Next} 동작일 경우 균일 제어 흐름으로 복귀한다고 보수적으로 가정합니다. 이는 위 표에서 결과 제어 흐름 노드가 입력 제어 흐름 노드와 동일하게 모델링됩니다.
15.2.7. 함수 호출의 균일성 규칙
가장 복잡한 규칙은 함수 호출에 대한 것입니다:
-
각 인자에 대해, 해당 표현식 규칙을 적용하고, 이전 인자 종료 시점의 제어 흐름을 사용합니다(첫 번째 인자의 경우 함수 호출 시작 시점의 제어 흐름을 사용). 해당 값 노드를 arg_i, 제어 흐름 노드를 CF_i라 명명합니다.
-
새로운 노드 두 개를 생성합니다: Result와 CF_after
-
함수의 call site tag가 CallSiteRequiredToBeUniform.S인 경우:
-
RequiredToBeUniform.S에서 마지막 CF_i로 에지를 추가합니다.
-
해당 call site tag의 potential-trigger-set에 있는 멤버들을 RequiredToBeUniform.S에 연결된 potential-trigger-set에 추가합니다.
-
-
함수의 function tag가 ReturnValueMayBeNonUniform라면 Result에서 MayBeNonUniform으로 에지를 추가합니다.
-
각 인자 i에 대해:
-
해당 parameter tag가 ParameterRequiredToBeUniform.S라면:
-
RequiredToBeUniform.S에서 arg_i로 에지를 추가합니다.
-
해당 parameter tag의 potential-trigger-set의 멤버들을 RequiredToBeUniform.S에 연결된 potential-trigger-set에 추가합니다.
-
-
parameter return tag가 ParameterReturnContentsRequiredToBeUniform라면, Result에서 arg_i로 에지를 추가합니다.
-
해당 파라미터의 pointer parameter tag가 PointerParameterMayBeNonUniform라면, Vout(call)에서 MayBeNonUniform으로 에지를 추가합니다.
-
해당 파라미터가 function 주소 공간의 포인터라면, Vout(call)에서 이전에 기록된 도달 가능한 각 arg_i로 에지를 추가합니다.
-
parameter tag가 ParameterContentsRequiredToBeUniform.S인 경우 RequiredToBeUniform.S에서 Vout(call)로 에지를 추가합니다.
-
-
참고: Vout(call)의 정의는 § 15.2.5 함수 범위 변수 값 분석을 참고하세요.
대부분의 내장 함수는 다음과 같은 태그를 가집니다:
-
각 파라미터별:
예외 리스트는 다음과 같습니다:
-
§ 17.11 Synchronization 내장 함수에 대한 호출:
-
call site tag: CallSiteRequiredToBeUniform.error, potential-trigger-set은 이름 없는 triggering rule을 포함합니다.
-
참고: 해당 triggering rule은 이름이 없으므로 필터링이 불가능합니다.
-
-
workgroupUniformLoad 호출의 경우, 파라미터
p
의 parameter tag는 ParameterRequiredToBeUniform.error이며, potential-trigger-set은 이름 없는 triggering rule을 포함합니다.
-
§ 17.6 Derivative 내장 함수, § 17.7.8 textureSample, § 17.7.9 textureSampleBias, § 17.7.10 textureSampleCompare에 대한 호출:
-
call site tag 규칙:
-
호출 위치와 triggering rule derivative_uniformity에 대한 가장 가까운 진단 필터 DF가 있다고 치자
-
DF가 있으면, S를 DF의 새로운 심각도 파라미터라 한다.
-
S가 off이면, call site tag는 CallSiteNoRestriction이다.
-
그 외에는 call site tag는 CallSiteRequiredToBeUniform.S이고, potential-trigger-set에는 derivative_uniformity 요소가 포함된다.
-
-
DF가 없다면, call site tag는 CallSiteRequiredToBeUniform.error이고, potential-trigger-set에는 derivative_uniformity 요소가 포함된다.
-
-
§ 17.7.4 textureLoad에 대한 호출:
-
§ 17.12 Subgroup 내장 함수 또는 § 17.13 Quad 연산에 대한 호출:
-
호출 위치와 triggering rule subgroup_uniformity에 대한 가장 가까운 진단 필터 DF가 있다고 치자
-
call site tag 규칙:
-
DF가 있으면, S를 DF의 새로운 심각도 파라미터라 한다.
-
S가 off이면, call site tag는 CallSiteNoRestriction이다.
-
그 외에는 call site tag는 CallSiteRequiredToBeUniform.S이고, potential-trigger-set에는 subgroup_uniformity 요소가 포함된다.
-
-
DF가 없다면, call site tag는 CallSiteRequiredToBeUniform.error이고, potential-trigger-set에는 subgroup_uniformity 요소가 포함된다.
-
-
subgroupShuffleUp 또는 subgroupShuffleDown 호출의 경우, 파라미터
delta
의 parameter tag 규칙:-
DF가 있으면, S를 DF의 새로운 심각도 파라미터라 한다.
-
S가 off이면, parameter tag는 NoRestriction이다.
-
그 외에는 parameter tag는 ParameterRequiredToBeUniform.S이고, potential-trigger-set에는 subgroup_uniformity 요소가 포함된다.
-
-
DF가 없다면, parameter tag는 ParameterRequiredToBeUniform.error이고, potential-trigger-set에는 subgroup_uniformity 요소가 포함된다.
-
-
subgroupShuffleXor 호출의 경우, 파라미터
mask
의 parameter tag 규칙:-
DF가 있으면, S를 DF의 새로운 심각도 파라미터라 한다.
-
S가 off이면, parameter tag는 NoRestriction이다.
-
그 외에는 parameter tag는 ParameterRequiredToBeUniform.S이고, potential-trigger-set에는 subgroup_uniformity 요소가 포함된다.
-
-
DF가 없다면, parameter tag는 ParameterRequiredToBeUniform.error이고, potential-trigger-set에는 subgroup_uniformity 요소가 포함된다.
-
참고: WGSL 구현체는 함수 호출 이전의 제어 흐름이 균일하다면, 호출 이후에도 균일하게 유지됨을 보장합니다.
15.2.8. 표현식의 균일성 규칙
표현식 분석 규칙은 표현식 자체와 그 시작 시점의 제어 흐름 노드(CF)를 인자로 받아, 다음을 반환합니다:
-
표현식 종료 시점의 제어 흐름에 해당하는 노드
-
해당 값에 해당하는 노드
-
그래프에 추가할 새로운 노드 및 에지 집합
표현식 | 새로운 노드 | 재귀적 분석 | 결과 제어 흐름 노드, 값 노드 | 새로운 에지 |
---|---|---|---|---|
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 rule이 MVE에서 타입 체크 중 호출됨 | Result | X는 이 표현식을 포함하는 문장의 입력 시점에서 "x" 값에 해당하는 노드 | CF, Result |
Result -> {CF, X}
참고: X는 "x"에 대한
Vout(prev)과 같음 |
identifier function-scope 변수 "x"로 해석됨, "x"가 디슈가된 포인터 파라미터 i이며, 해당 identifier가 memory view 표현식 MVE의 루트 identifier로 등장하며, load rule이 MVE에서 타입 체크 중 호출되지 않음 | CF, param_i | |||
identifier function-scope 변수 "x"로 해석됨, 해당 identifier가 memory view 표현식 MVE의 루트 identifier로 등장하며, load rule이 MVE에서 타입 체크 중 호출되지 않음 | 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 rule이 MVE에서 타입 체크 중 호출됨 | CF, MayBeNonUniform | |||
identifier storage, workgroup, private 주소 공간의 포인터 타입 formal parameter "x"로 해석됨, non-read-only 접근 모드이며, identifier가 memory view 표현식 MVE의 루트 identifier로 등장, load rule이 MVE에서 타입 체크 중 호출되지 않음 | 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 rule이 MVE에서 타입 체크 중 호출됨 | CF, MayBeNonUniform | |||
identifier 모듈 범위 비읽기 전용 변수 "x"로 해석됨, identifier가 memory view 표현식 MVE의 루트 identifier로 등장, load rule이 MVE에서 타입 체크 중 호출되지 않음 | 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] |
다음 내장 입력 변수는 균일한 것으로 간주됩니다:
-
subgroup_size (단, 컴퓨트 셰이더 스테이지에서 사용될 때)
그 외 모든 내장 입력 값(built-in values 참고)은 비균일로 간주됩니다.
참고: 작성자는 균일 내장 값을 다른 비균일 입력과 함께 그룹화하는 것을 피해야 합니다. 분석은 컴포넌트별로 합성 타입을 별도로 분석하지 않습니다.
표현식 | 새로운 노드 | 재귀적 분석 | 결과 제어 흐름 노드, 변수 노드 | 새로운 에지 |
---|---|---|---|---|
identifier function-scope 변수 "x"로 해석됨 | Result | X는 이 표현식을 포함하는 문장의 출력 시점에서 "x" 값에 해당하는 노드. | CF, Result |
Result -> {CF, X}
참고: X는 "x"에 대한
Vin(next)과 같음 |
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. 모든 제어 흐름 지점의 균일성 주석 달기
이 전체 하위 섹션은 규범적인 내용이 아닙니다.
구현자가 셰이더 전체의 제어 흐름 각 지점에서 해당 지점이 균일한지 여부(따라서 그 지점에서 균일성이 요구되는 함수를 호출해도 유효한지 여부)를 진단 모드로 개발자에게 보여주고 싶다면, 아래를 제안합니다:
-
앞선 하위 섹션에서 설명한 (필수, 규범적인) 분석을 실행하고, 모든 함수에 대해 그래프를 유지합니다.
-
모든 그래프의 에지를 반전시킵니다.
-
각 함수를, 엔트리 포인트부터 시작해서 모든 호출자를 방문하기 전에는 해당 함수를 방문하지 않는 방식으로 처리합니다:
-
적어도 하나의 호출자에서 비균일했던 모든 인자에 MayBeNonUniform에서 에지를 추가합니다.
-
적어도 하나의 호출자에서 비균일 제어 흐름으로 함수가 호출된 경우 MayBeNonUniform에서 CF_start로 에지를 추가합니다.
-
MayBeNonUniform에서 도달 가능한 노드가 무엇인지 확인합니다. 방문된 모든 노드는 분석에 의해 균일성을 증명할 수 없는 표현식 또는 제어 흐름 지점입니다.
-
이러한 도달성 분석에 의해 방문되지 않은 노드는 분석에 의해 균일성임이 증명될 수 있습니다(그래서 그 지점에서 도함수 등 함수를 호출해도 안전합니다).
참고: 그래프 내 호출을 만날 때 추가할 에지를 알 수 있으므로, 하향식(bottom-up) 분석은 여전히 필요합니다.
15.2.10. 예제
다음 예제의 그래프는 노드에 대해 다음과 같은 관례를 사용합니다:
-
직사각형은 값 노드를 나타냅니다.
-
둥근 직사각형은 제어 흐름 노드를 나타냅니다.
15.2.10.1.
잘못된 textureSample
함수 호출
이 예제는 textureSample
내장 함수 호출의 잘못된 사용을 보여줍니다.
함수 호출이 if 문 내에서 이루어지며, 조건이 비균일 값(즉, 내장 값 position
)에 따라 결정됩니다.
잘못된 의존성 체인은 빨간색으로 강조됩니다.
@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 함수 호출이 함수 범위 변수 값에 의존하는 경우의 유효 및 무효 예시를 보여줍니다.
workgroupBarrier
는 x
값이 변경 가능한 모듈 범위 변수 a
에서 유도되었으므로 무효입니다.
storageBarrier
는 x
값이 불변 모듈 범위 변수 b
에서 유도되었으므로 유효합니다.
이 예제는 함수 범위 변수의 생명주기에서 균일성의 구간을 분석하는 값 분석의 능력을 강조합니다.
또한 첫 번째 if 문 끝에서 제어 흐름이 다시 균일해짐을
분명하게 보여줍니다.
해당 그래프 부분이 두 번째 if 문과 독립적임을 알 수 있습니다.
@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. 합성 값 분석의 한계
균일성 분석의 한계 중 하나는 합성 값의 각 컴포넌트를 독립적으로 추적하지 않는다는 점입니다. 즉, 비균일 컴포넌트 값이 있으면 분석은 전체 합성 값을 비균일로 처리합니다. 이 예제는 해당 문제와 셰이더 개발자가 이 한계를 피하기 위해 사용할 수 있는 잠재적 우회 방법을 보여줍니다.
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으로 가는 경로가 사라진 것을 볼 수 있습니다.
@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)에 의존합니다.
@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 tag를
ParameterReturnContentsRequiredToBeUniform로
설정합니다.
이로 인해 main
에서 scale
함수 호출의 반환 값에서 position
내장 값까지의 경로가 생깁니다.
해당 경로는 전체적으로 RequiredToBeUniform.S에서
MayBeNonUniform으로 이어지는
잘못된 경로의 하위 경로입니다.
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 ); } }
참고: 서브그래프는 이해를 돕기 위한 예시로만 포함되어 있습니다.
15.3. 컴퓨트 셰이더와 워크그룹
워크그룹이란 동시에 컴퓨트 셰이더 스테이지의 엔트리 포인트를 실행하며, workgroup 주소 공간의 셰이더 변수에 대한 접근을 공유하는 호출 집합을 의미합니다.
컴퓨트 셰이더의 워크그룹 그리드란 정수 좌표 (i,j,k)로 이루어진 점들의 집합으로, 다음을 만족합니다:
-
0 ≤ i < workgroup_size_x
-
0 ≤ j < workgroup_size_y
-
0 ≤ k < workgroup_size_z
여기서 (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)로 이루어진 점들의 집합으로 다음을 만족합니다:
-
0 ≤ CSi < workgroup_size_x × group_count_x
-
0 ≤ CSj < workgroup_size_y × group_count_y
-
0 ≤ CSk < workgroup_size_z × group_count_z
여기서 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 0: 좌상단 호출
-
ID 1: 우상단 호출
-
ID 2: 좌하단 호출
-
ID 3: 우하단 호출
참고: 쿼드 ID에 접근하는 내장 값 accessor는 존재하지 않습니다.
일반적으로 프래그먼트 처리는 RasterizationPoint마다 하나의 프래그먼트 셰이더 호출을 생성합니다 (래스터화에서 생성됨). 때때로 그래픽 프리미티브의 경계에서 쿼드를 완전히 채울 만큼의 RasterizationPoint가 부족할 수 있습니다. 쿼드에 1, 2, 또는 3개의 호출만 RasterizationPoint에 대응하면, 프래그먼트 처리는 쿼드의 비어있는 위치마다 헬퍼 호출(helper invocation)을 생성합니다.
헬퍼 호출은 관찰 가능한 효과를 가지지 않으며, 오직 도함수 계산을 돕는 역할만 합니다. 따라서 헬퍼 호출에는 다음과 같은 제한이 적용됩니다:
-
쓰기 접근(자세한 내용은 § 14.5.1 메모리 연산 참고)은 storage 또는 handle 주소 공간에서 수행되지 않습니다.
-
Atomic 내장 함수는 불확정(indeterminate) 결과를 반환합니다.
-
엔트리 포인트의 반환 값은 GPURenderPipeline의 다운스트림에서 추가 처리가 이루어지지 않습니다.
쿼드의 모든 호출이 헬퍼 호출이 되는 경우(예: 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_id
와 local_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 내장 함수에 설명된 내장 함수로 계산됩니다:
-
dpdx, dpdxCoarse, dpdxFine는 x축 방향 편미분을 계산합니다.
-
dpdy, dpdyCoarse, dpdyFine는 y축 방향 편미분을 계산합니다.
-
fwidth, fwidthCoarse, fwidthFine 는 관련된 x, y 편미분의 맨해튼 거리를 계산합니다.
인접 호출들이 함께 도함수를 계산하므로, 이 함수들은 프래그먼트 셰이더에서 반드시 균일 제어 흐름에서만 호출해야 합니다. 이러한 함수 각각의 호출에 대해, 만약 균일성 분석이 호출이 균일 제어 흐름에서 발생하는지 증명하지 못한다면, 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 이진 부동소수점 타입은 확장 실수 수직선을 다음과 같이 근사합니다:
-
타입은 유한한 값의 집합을 가지며, 다음과 같은 구분된 카테고리를 포함합니다:
-
양수와 음수 유리수.
-
각 값은 유한하며, 아래에서 정의한 정규(normal) 혹은 서브노멀(subnormal)입니다.
-
-
NaN 값. NaN은 "Not a Number"의 약자로 잘못된 연산의 결과를 나타냅니다. IEEE-754는 에러 보고와 관련된 시그널 NaN과 조용한 NaN을 모두 요구합니다. WGSL은 그러한 에러 보고를 요구하지 않으며, NaN 대신 불확정 값을 반환할 수 있습니다. § 15.7.2 IEEE-754와의 차이점 참고.
-
-
타입은 다음과 같은 연산을 지원합니다:
-
타입은 다음과 같은 비트 표현을 가집니다:
-
고정된 비트 폭을 가지며, 각 값의 비트 표현은 최상위 비트부터 순서대로 세 개의 연속된 비트 필드로 구성됩니다:
-
1비트 부호 필드(sign field).
-
고정 폭 지수 필드(exponent field).
-
고정 폭 가수 필드(trailing significand field).
-
-
지수 필드 해석에 관련된 정수값 지수 바이어스(exponent bias).
-
부동소수점 타입의 유한 범위(finite range)란 구간(interval) [low, high]으로, low는 타입 내에서 가장 작은 유한 값이고, high는 가장 큰 유한 값입니다.
관심 있는 IEEE-754 부동소수점 타입은 다음과 같습니다:
-
binary16:
-
유한 범위: [−65504, 65504]
-
binary32:
-
유한 범위: [ − (2 − 2−23) × 2127, (2 − 2−23) × 2127 ], 대략적으로 [ − 3.4028235 × 1038, 3.4028235 × 1038 ].
-
binary64:
-
유한 범위: [ − (2 − 2−52) × 21023, (2 − 2−52) × 21023 ], 대략적으로 [ − 1.798 × 10308, 1.798 × 10308 ].
다음 알고리즘은 부동소수점 값의 비트 표현을 해당 확장 실수 값이나 NaN으로 매핑합니다:
알고리즘: 비트의 부동소수점 해석입력: Bits, 이진 부동소수점 타입의 값에 대한 비트 표현.
출력: F, Bits가 나타내는 부동소수점 값.
절차:
bias를 타입의 지수 바이어스로 한다.
tsw를 타입의 가수 필드(trailing significand field)의 비트 폭으로 한다.
Bits를 부호 필드(sign field), 지수 필드(exponent field), 가수 필드(trailing significand field)로 분할한다.
각 필드를 부호 없는 정수로 해석해서 Sign, E, T라 한다.
지수 필드가 모두 1이면:
Sign = 0이고 T = 0이면 F = +∞.
Sign = 1이고 T = 0이면 F = −∞.
T ≠ 0이면 F는 NaN이다.
그 외에 지수 필드가 모두 0이면:
F = (− 1)Sign × 2−bias × T × 2−tsw+1.
T = 0이면 값은 0이다.
각 부동소수점 타입은 양의 0과 음의 0을 모두 가진다. 음의 0(negative zero)은 부호(sign) 비트가
1
인 0 값이다. 음의 0과 양의 0은 비교할 때 같다고 취급된다. IEEE-754는 WGSL에서 중요하지 않은 특정 경계 케이스를 나타내기 위해 음의 0을 사용한다.T ≠ 0이면 값 F는 서브노멀(subnormal)이다. (디노멀라이즈드(denormalized)는 서브노멀과 동의어.)
그 외의 경우 지수 필드가 모두 1도 아니고 모두 0도 아니면:
F = (− 1)Sign × 2(E−bias) × ( 1 + T × 2−tsw).
값 F는 정규(normal)이다.
부동소수점 연산의 도메인(domain)은 해당 연산이 잘 정의된 확장 실수 입력값 집합입니다.
-
예를 들어, 수학 함수 √의 도메인은 구간 [0,+∞]입니다. √는 0보다 작은 입력에 대해 잘 정의되지 않습니다.
-
연산이 도메인 내부에서 평가될 때, 연산은 무한 정확도의 확장 실수 중간 결과(intermediate result)로 정의된 후, 반올림(rounding)을 통해 부동소수점 결과로 변환됩니다.
-
도메인 외부에서 평가될 때, IEEE-754의 기본 예외 처리 규칙에 따라 구현체는 예외를 발생시키고 NaN 값을 반환해야 합니다. WGSL은 부동소수점 예외를 필수로 요구하지 않으며, 대신 불확정 값을 반환할 수 있습니다. § 15.7.2 IEEE-754와의 차이점 참고.
반올림(rounding)은 확장 실수 값 x를 부동소수점 타입의 값 x'로 매핑합니다. x가 이미 해당 부동소수점 타입의 값일 경우, 반올림은 x를 자기 자신으로 매핑합니다: x = x'. x가 타입의 유한 범위 밖이면 반올림은 오버플로우(overflow)가 발생할 수 있습니다. 그 외의 경우 x'는 x보다 위쪽에 있는 가장 작은 부동소수점 값이거나 아래쪽에 있는 가장 큰 부동소수점 값입니다; 반올림 모드(rounding mode)가 어느 쪽을 선택할지 결정합니다.
일반적으로, NaN 입력이 있는 연산은 NaN 출력을 반환합니다. 예외로는:
-
NaN은 어떤 부동소수점 값과도 같지 않고, 작거나 크지도 않습니다. 이런 비교는 항상 false를 반환합니다.
IEEE-754는 다섯 종류의 예외(exception)를 정의합니다:
-
잘못된 연산(invalid operation). 연산이 확장 실수 입력에 대해 도메인 밖에서 평가될 때 발생합니다. 이런 연산은 NaN을 반환합니다. 예: 0 × +∞,
sqrt
(−1). -
0으로 나누기(division by zero). 유한 피연산자에 대해 연산 결과가 정확히 무한대일 때 발생합니다. 예: 1 ÷ 0, log(0).
-
오버플로우(overflow). 중간 결과가 유한 범위를 초과할 때 발생합니다. § 15.7.3 부동소수점 반올림 및 오버플로우 참고.
-
부정확(inexact). 반올림 결과가 중간 결과와 다를 때, 혹은 오버플로우가 발생할 때입니다.
15.7.2. IEEE-754와의 차이점
WGSL은 IEEE-754 표준을 따르지만, 다음과 같은 차이가 있습니다:
-
부동소수점 값 x를 정수형으로 변환할 때, x는 먼저 대상 타입의 값 범위로 클램핑(clamp)됩니다. § 15.7.6 부동소수점 변환 참고.
-
부동소수점 예외가 생성되지 않습니다.
-
WGSL에서의 부동소수점 연산은 반드시 IEEE-754 규칙에 따라 중간 결과를 생성하지만, IEEE-754에서 요구하는 예외는 해당 표현식이 const-expression, override-expression, 런타임 표현식 중 어떤 것인가에 따라 다른 동작으로 매핑됩니다.
-
유한 피연산자에 대한 연산을 예로 듭니다. 연산의 중간 결과가 오버플로우, 무한대, 또는 NaN을 생성하는 경우는 IEEE-754에서 오버플로우, 잘못된 연산, 0으로 나누기 예외를 신호하도록 요구하는 경우에만 해당합니다. 동작은 유한 수학 가정(Finite Math Assumption)에 따라 추가로 수정될 수 있습니다.
-
-
시그널링 NaN은 생성되지 않을 수 있습니다. 중간 계산에서 시그널링 NaN은 조용한 NaN으로 변환될 수 있습니다.
-
유한 수학 가정(Finite Math Assumption):
-
오버플로우, 무한대, NaN이 셰이더 실행 시작 전에 생성되면 오류가 발생합니다.
-
const-expression 및 override-expression이 유한 값에 대해 평가될 때, 오버플로우, 무한대, NaN을 IEEE-754 규칙에 따라 중간 결과 값으로 생성합니다.
-
참고: 이 규칙은 구현체가 이러한 표현식들에 대해 오버플로우, 무한대, NaN을 정확도 한계 내에서 신뢰성 있게 검출하여 오류를 일관되게 발생시켜야 함을 요구합니다.
-
-
부동소수점 타입의 const-expression에서 오버플로우, NaN, 무한대가 평가된 경우 셰이더 생성 오류가 발생합니다.
-
부동소수점 타입의 override-expression에서 오버플로우, NaN, 무한대가 평가된 경우 파이프라인 생성 오류가 발생합니다.
-
-
구현체는 셰이더 실행 동안 오버플로우, 무한대, NaN이 존재하지 않는다고 가정할 수 있습니다.
-
-
구현체는 부동소수점 0 값의 부호 필드를 무시할 수 있습니다. 즉, 양의 부호를 가진 0이 음의 부호를 가진 0처럼 작동할 수 있고, 그 반대도 가능합니다.
-
제로로 플러시(flush to zero)란 서브노멀 부동소수점 값을 해당 타입의 0 값으로 대체하는 것입니다.
-
§ 15.7.4 부동소수점 정확도에 명시된 연산의 입력 또는 출력은 제로로 플러시될 수 있습니다.
-
또한, 중간 결과 값이 § 17.2 비트 재해석 내장 함수, § 17.9 데이터 패킹 내장 함수, § 17.10 데이터 언패킹 내장 함수의 연산일 경우 제로로 플러시될 수 있습니다.
-
그 외의 연산은 서브노멀 값을 보존해야 합니다.
-
-
연산의 정확도는 § 15.7.4 부동소수점 정확도에 명시되어 있습니다.
-
WGSL의 일부 내장 함수는 IEEE-754 연산과 의미가 다릅니다. 이런 경우는 WGSL 내장 함수 정의에서 별도로 명시됩니다.
예를 들어 WGSL의 § 17.5.32 fma 함수는 일반 곱셈(반올림 포함)과 덧셈(반올림 포함)으로 확장될 수 있지만, IEEE-754의
fusedMultiplyAdd
연산은 마지막 반올림만 허용해야 합니다.
15.7.3. 부동소수점 반올림 및 오버플로우
오버플로우가 발생하는 계산은 무한대로 반올림하거나, 가장 가까운 유한 값으로 반올림할 수 있습니다. 결과는 오버플로우 중간 결과의 크기와, 평가가 셰이더 모듈 생성, 파이프라인 생성, 셰이더 실행 중 언제 일어났는지에 따라 달라집니다.
부동소수점 타입 T에 대해, MAX(T)는 T의 가장 큰 양의 유한 값이며, 2EMAX(T)는 T로 표현 가능한 가장 큰 2의 거듭제곱입니다. 예를 들어, EMAX(f32) = 127, EMAX(f16) = 15입니다.
부동소수점 계산의 무한 정확도 중간 결과 X가 있다고 할 때, 표현식의 최종 값은 두 단계의 중간 결과 값 X', X''를 통해 결정됩니다:
X로부터 X'를 T에서 반올림하여 계산합니다:
-
X가 T의 유한 범위 내에 있으면, X'는 X를 위/아래로 반올림한 결과입니다.
-
X가 NaN이면 X'는 NaN입니다.
-
MAX(T) < X < 2EMAX(T)+1이면, 어느 방향의 반올림도 허용됩니다: X'는 MAX(T) 또는 +∞입니다.
-
2EMAX(T)+1 ≤ X이면 X' = +∞입니다.
-
참고: 이 규정은 IEEE-754 규칙과 일치합니다.
-
-
−MAX(T) > X > −2EMAX(T)+1이면, 어느 방향의 반올림도 허용됩니다: X'는 −MAX(T) 또는 −∞입니다.
-
−2EMAX(T)+1 ≥ X이면 X' = −∞입니다.
-
참고: 이 규정은 IEEE-754 규칙과 일치합니다.
-
X'로부터 표현식의 최종 값 X''를 계산하거나 프로그램 오류를 검출합니다:
-
X'가 무한대 또는 NaN이면 유한 수학 가정에 따라:
-
표현식이 const-expression이면 셰이더 생성 오류를 발생시킵니다.
-
표현식이 override-expression이면 파이프라인 생성 오류를 발생시킵니다.
-
-
그 외의 경우 X'' = X'입니다.
15.7.4. 부동소수점 정확도
-
x가 T에 속하면 x
-
그 외의 경우:
-
x보다 큰 T 내에서 가장 작은 값, 또는
-
x보다 작은 T 내에서 가장 큰 값
-
즉, 결과는 위/아래 반올림될 수 있다. 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]:
-
x
가 해당 부동소수점 타입의 유한 범위 내에 있으면, ULP(x)는a
≤x
≤b
인 두 서로 다른 유한 부동소수점 값a
,b
사이의 최소 거리 (ulp(x) = min
a,b
|b - a|
)이다. -
그 외의 경우, ULP(x)는
b
,a
가 표현 가능한 가장 큰, 두 번째로 큰 유한 부동소수점 값일 때|b - a|
이다.
연산의 정확도는 다섯 가지 중 하나로 제공된다:
-
정확한 결과(비부동소수점 결과값에 대해)
-
절대 오차 경계
-
ULP로 표현되는 상대 오차 경계
-
정확도가 상속됨(inherited from)임을 표현 즉, 연산의 정확도는 지정된 WGSL 표현식을 평가한 정확도와 동일함을 의미한다. 지정된 표현식은 함수의 유효한 구현 중 하나일 뿐이다.
상속함 표현식을 평가할 때, 하위 표현식의 평가도 부동소수점 평가 규칙(반올림, 오버플로우, 재결합, fusion, 제로로 플러시 등)에 따라야 한다.
WebGPU 구현체는 동일 연산을 더 높은 정확도 또는 더 극단적인 입력 허용을 가진 방식으로 구현할 수 있다.
연산의 정확도가 입력 범위에 대해 지정되어 있으면, 해당 범위를 벗어난 입력값에 대한 정확도는 정의되지 않는다.
허용된 결과가 결과 타입의 유한 범위 밖에 있으면, § 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)
|
다음 중 더 나쁜 값:
|
다음 중 더 나쁜 값:
|
acosh(x)
| log(x + sqrt(x * x - 1.0)) 에서 상속
| |
asin(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)
|
올바른 반올림
무한정 정밀도의 결과는
| |
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]) 에서 상속(단,
i ≠ j )
| |
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의 순진한 계산( 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에서 서로 다른 호출 간 값의 차이(혹은 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)
|
다음 중 더 나쁜 값:
| |
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 연산의 정확도는 다음과 같습니다:
-
해당 f32 연산이 정확한 결과를 요구하는 경우, 정확한 결과가 필요합니다.
-
fract(x)
의 오차는x - floor(x)
에서 상속되며, 중간 계산은 AbstractFloat 연산으로 수행됩니다. -
그 외의 경우, 해당 f32 연산의 오차가 절대 오차, 상대 오차, 잠재적 구현에서 상속된 오차, 또는 이들의 조합일 때, AbstractFloat의 오차는 제한되지 않습니다.
-
단, AbstractFloat 연산의 오차는 해당 f32 연산의 오차보다 절대적으로 크지 않아야 합니다.
-
이 권고는 의도치 않은 결과를 방지하기 위한 것으로, 표현식의 타입을 f32에서 AbstractFloat로 변경할 때 정확도가 줄어들지 않도록 합니다.
-
연산은 WebAssembly [WASM-CORE-2] 또는 ECMAScript [ECMASCRIPT] 환경에서 평가될 수 있으며, 해당 명세에서는 많은 수치 계산에 대해 오차 경계를 명시하지 않습니다. 예를 들어, ECMAScript는 많은 부동소수점 연산을 구현 근사(implementation-approximated)로 명시합니다. 구현체는 이상적인 값을 근사하려 노력해야 하지만, 엄격한 요구사항은 없습니다.
-
ULP는 AbstractFloat 값의 경우 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)이란 연산을 정확하게 계산했을 때 결과가 같은 방식으로 표현식을 재배열하는 것을 의미합니다. 예시:
-
(a + b) + c
는a + (b + c)
로 재결합됩니다. -
(a - b) + c
는(a + c) - b
로 재결합됩니다. -
(a * b) / c
는(a / c) * b
로 재결합됩니다.
하지만, 부동소수점에서 계산할 때 결과가 같지 않을 수 있습니다. 재결합된 결과는 근사 오차로 인해 부정확하거나, 중간 결과 계산 시 오버플로우 또는 NaN을 유발할 수 있습니다.
구현체는 연산을 재결합할 수 있습니다.
구현체는 변환된 표현식이 원래 표현식만큼 정확하거나 더 정확하면 연산을 결합(fuse)할 수 있습니다. 예를 들어, 일부 곱셈-덧셈 결합(fused multiply-add) 구현은 곱셈 후 덧셈을 따로 수행하는 것보다 더 정확할 수 있습니다.
15.7.6. 부동소수점 변환
이 섹션은 소스 또는 목적지가 부동소수점 타입인 스칼라 변환의 세부사항을 설명합니다.
이 섹션에서 부동소수점 타입은 다음 중 하나일 수 있습니다:
-
WGSL의 f32, f16, AbstractFloat 타입
-
IEEE-754 부동소수점 표준에 정의된 이진 형식에 대응하는 가상 타입
참고: f32 WGSL 타입은 IEEE-754 binary32 형식에, f16 WGSL 타입은 IEEE-754 binary16 형식에 대응함을 기억하세요.
스칼라 부동소수점 → 정수 변환 알고리즘:
부동소수점 스칼라 값 X를 integer scalar 타입 T로 변환하려면:
참고: 즉, NaN이 아닌 경우 부동소수점→정수 변환은 값을 대상 타입 범위 내로 클램핑한 뒤 0 방향으로 반올림합니다. 이 클램핑 요구사항은 WGSL이 의미 있는 결과를 요구하는 부분이며, C/C++에서는 미정의 동작이고, IEEE-754에서는 잘못된 연산 예외 + NaN 결과를 요구합니다.
수치 스칼라 → 부동소수점 변환 알고리즘:
알고리즘: 수치 스칼라 변환 → 부동소수점입력:
X: 타입 S의 수치 스칼라 값
T: 목적지 부동소수점 타입
출력: XOut: X를 T로 변환한 결과, 또는 오류 발생
절차:
X가 소스 타입 S에서 NaN이면, XOut은 타입 T의 NaN
X가 목적지 타입 T에서 정확히 표현 가능하면, XOut은 X와 같은 T의 값
추가로, X가 0이고 integer scalar 타입이면 XOut은 0 부호 비트를 가짐
그 외의 경우 X가 T에서 정확히 표현 불가능하면:
X가 T 내 인접한 두 유한 값 사이에 있으면, XOut은 그 두 값 중 하나 WGSL은 높은 값/낮은 값을 선택하는지 지정하지 않으며, 변환마다 다를 수 있음
그 외에 X가 목적지 타입 T의 유한 범위 밖에 있으면:
X의 표현식이 const-expression이면 셰이더 생성 오류 발생
X의 표현식이 override-expression이면 파이프라인 생성 오류 발생
그 외의 경우 변환은 다음과 같이 진행:
X'를 원래 값 X로 설정
소스 타입 S가 목적지 타입 T보다 더 많은 가수 비트를 가진 부동소수점 타입인 경우, 추가 가수 비트는 버려질 수 있음(0으로 취급). X'를 갱신
X'가 목적지 타입 T의 최댓값 또는 최솟값이면 XOut = X'
그 외에는 XOut을 X'와 같은 부호의 목적지 타입 T의 무한대로 설정
참고: 정수 값은 인접한 두 부동소수점 값 사이에 있을 수 있습니다. 예를 들어, 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 부동소수점 정확도에서는 이런 연산을 상속됨 정확도로 분류합니다. 도메인을 명시할 때, 다음 중 하나입니다:
-
도메인이 명시적으로 설명됨
-
도메인이 선형 항에서 유추(implied from linear terms)라면, 도메인은 다음 방식으로 도출됩니다:
-
원래 연산이 "상속됨" 표현식으로 대체되었다고 가정하고, 이는 부동소수점 덧셈/뺄셈/곱셈의 조합입니다.
-
남은 연산에 대해 해당 파라미터에 대한 도메인 제한을 적용 및 결합합니다.
-
예시: dot(a,b)
함수가 2-요소 벡터 a, b에 대해 정확도가 상속됨으로 분류됩니다.
a[0] * b[0] + a[1] * b[1] 표현을 사용하며,
두 번의 부동소수점 곱셈과 한 번의 덧셈이 사용됩니다.
-
부동소수점 곱셈은 한 피연산자가 0이고 다른 피연산자가 무한대일 때를 제외하고 확장 실수에서 잘 정의됩니다.
-
부동소수점 덧셈은 두 피연산자가 서로 반대 부호의 무한대일 때를 제외하고 잘 정의됩니다.
-
따라서 도메인은 모든 확장 실수의 2-요소 벡터 쌍 a와 b이지만, 다음은 제외됩니다:
-
곱셈에서 유도됨:
-
a[i]가 0이고 b[i]가 무한대일 때.
-
a[i]가 무한대이고 b[i]가 0일 때.
-
-
덧셈에서 유도됨:
-
a[0] × b[0]이 +∞이고 a[1] × b[1]이 +∞일 때
-
a[0] × b[0]이 −∞이고 a[1] × b[1]이 −∞일 때
-
-
16. 키워드 및 토큰 요약
16.1. 키워드 요약
-
alias
-
break
-
case
-
const
-
const_assert
-
continue
-
continuing
-
default
-
diagnostic
-
discard
-
else
-
enable
-
false
-
fn
-
for
-
if
-
let
-
loop
-
override
-
requires
-
return
-
struct
-
switch
-
true
-
var
-
while
16.2. 예약어
예약어는 토큰으로, 추후 사용을 위해 예약되어 있습니다. WGSL 모듈은 예약어를 포함해서는 안 됩니다.
다음은 예약어입니다:
| '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. 구문 토큰
구문 토큰은 특별한 코드 포인트의 시퀀스로, 다음과 같이 사용됩니다:
-
식 연산자를 표기할 때, 또는
-
구두점으로서: 다른 문법 요소를 그룹화, 순서 지정, 또는 분리할 때.
구문 토큰은 다음과 같습니다:
-
'&'
(코드 포인트:U+0026
) -
'&&'
(코드 포인트:U+0026
U+0026
) -
'->'
(코드 포인트:U+002D
U+003E
) -
'@'
(코드 포인트:U+0040
) -
'/'
(코드 포인트:U+002F
) -
'!'
(코드 포인트:U+0021
) -
'['
(코드 포인트:U+005B
) -
']'
(코드 포인트:U+005D
) -
'{'
(코드 포인트:U+007B
) -
'}'
(코드 포인트:U+007D
) -
':'
(코드 포인트:U+003A
) -
','
(코드 포인트:U+002C
) -
'='
(코드 포인트:U+003D
) -
'=='
(코드 포인트:U+003D
U+003D
) -
'!='
(코드 포인트:U+0021
U+003D
) -
'>'
(코드 포인트:U+003E
) (또한 템플릿 구분을 위해_greater_than
사용) -
'>='
(코드 포인트:U+003E
U+003D
) (또한 템플릿 구분을 위해_greater_than_equal
사용) -
'>>'
(코드 포인트:U+003E
U+003E
) (또한 템플릿 구분을 위해_shift_right
사용) -
'<'
(코드 포인트:U+003C
) (또한 템플릿 구분을 위해_less_than
사용) -
'<='
(코드 포인트:U+003C
U+003D
) (또한 템플릿 구분을 위해_less_than_equal
사용) -
'<<'
(코드 포인트:U+003C
U+003C
) (또한 템플릿 구분을 위해_shift_left
사용) -
'%'
(코드 포인트:U+0025
) -
'-'
(코드 포인트:U+002D
) -
'--'
(코드 포인트:U+002D
U+002D
) -
'.'
(코드 포인트:U+002E
) -
'+'
(코드 포인트:U+002B
) -
'++'
(코드 포인트:U+002B
U+002B
) -
'|'
(코드 포인트:U+007C
) -
'||'
(코드 포인트:U+007C
U+007C
) -
'('
(코드 포인트:U+0028
) -
')'
(코드 포인트:U+0029
) -
';'
(코드 포인트:U+003B
) -
'*'
(코드 포인트:U+002A
) -
'~'
(코드 포인트:U+007E
) -
'_'
(코드 포인트:U+005F
) -
'^'
(코드 포인트:U+005E
) -
'+='
(코드 포인트:U+002B
U+003D
) -
'-='
(코드 포인트:U+002D
U+003D
) -
'*='
(코드 포인트:U+002A
U+003D
) -
'/='
(코드 포인트:U+002F
U+003D
) -
'%='
(코드 포인트:U+0025
U+003D
) -
'&='
(코드 포인트:U+0026
U+003D
) -
'|='
(코드 포인트:U+007C
U+003D
) -
'^='
(코드 포인트:U+005E
U+003D
) -
'>>='
(코드 포인트:U+003E
U+003E
U+003D
) (또한 템플릿 구분을 위해_shift_right_assign
사용) -
'<<='
(코드 포인트:U+003C
U+003C
U+003D
) (또한 템플릿 구분을 위해_shift_left_assign
사용) -
_template_args_end
-
텍스트:
'>'
(코드 포인트:U+003E
) -
이 토큰은 greater_than 구문 토큰과 텍스트상 동일합니다.
-
템플릿 리스트 구분에서 생성되며, 템플릿 리스트의 마지막 토큰으로 사용됩니다.
-
-
_template_args_start
-
텍스트:
'<'
(코드 포인트:U+003C
) -
이 토큰은 less_than 구문 토큰과 텍스트상 동일합니다.
-
템플릿 리스트 구분에서 생성되며, 템플릿 리스트의 첫 번째 토큰으로 사용됩니다.
-
-
_disambiguate_template
-
텍스트: 없음
-
이 토큰은 파서에게 템플릿 리스트를 탐색하도록 알립니다.
-
템플릿 리스트 구분을 트리거합니다.
-
17. 내장 함수
특정 함수들은 사전 선언되어 있으며, 구현에 의해 제공되고, 따라서 WGSL 모듈에서 항상 사용할 수 있습니다. 이러한 함수들을 내장 함수라고 합니다.
내장 함수는 동일한 이름을 가지지만, 매개변수의 개수, 순서, 타입에 따라 구분되는 함수군입니다. 이러한 각각의 함수 변형을 오버로드라고 합니다.
참고: 각 사용자 정의 함수는 하나의 오버로드만 가집니다.
각 오버로드는 아래에 다음과 같이 설명됩니다:
내장 함수를 호출할 때, 함수의 모든 인자는 함수 평가가 시작되기 전에 평가됩니다. § 11.2 함수 호출을 참고하세요.
17.1. 생성자 내장 함수
값 생성자 내장 함수는 명시적으로 주어진 타입의 값을 생성합니다.
WGSL은 모든 선언된 타입과 모든 생성 가능 구조체 타입에 대해 값 생성자를 제공합니다. 생성자 내장 함수는 타입과 동일한 이름을 가집니다. 이러한 내장 함수가 사용되는 모든 곳에서, 타입의 식별자가 스코프 내에 있어야 하며, 식별자가 다른 선언으로 해결되지 않아야 합니다.
참고: frexp, modf, 그리고 atomicCompareExchangeWeak에 의해 반환되는 구조체 타입들은 WGSL 모듈에서 작성할 수 없습니다.
참고: 해당 타입의 값 선언은 WGSL 텍스트의 해당 구문에서 유효해야 합니다.
WGSL은 두 가지 종류의 값 생성자를 제공합니다:
17.1.1. 제로 값 내장 함수
각각의 구체적, 생성 가능한 T는 고유한 제로 값을 가지며,
해당 타입 이름 뒤에 빈 괄호를 붙여 WGSL에서 내장 함수로 작성됩니다:
T ()
.
추상 수치 타입도 제로
값을 가지지만, 이를 접근할 수 있는 내장 함수는 없습니다.
제로 값은 다음과 같습니다:
-
bool()
은false
입니다 -
i32()
는 0i입니다 -
u32()
는 0u입니다 -
f32()
는 0.0f입니다 -
f16()
는 0.0h입니다 -
T 타입의 N-컴포넌트 벡터의 제로 값은 T의 제로 값으로 구성된 N-컴포넌트 벡터입니다.
-
T 타입의 C-컬럼 R-행 행렬의 제로 값은 해당 차원의 모든 원소가 T의 제로 값인 행렬입니다.
-
E 요소 타입을 가지는 생성 가능한 N 요소 배열의 제로 값은 E의 제로 값으로 구성된 N 요소 배열입니다.
-
생성 가능한 구조체 타입 S의 제로 값은 모든 멤버가 제로 값인 S 구조체 값입니다.
-
AbstractInt의 제로 값은 0입니다.
-
AbstractFloat의 제로 값은 0.0입니다.
참고: WGSL은 atomic 타입, 실행 시 크기가 결정되는 배열, 또는 생성 불가 타입에 대한 제로 내장 함수를 제공하지 않습니다.
오버로드 |
|
파라미터화 | 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
오버로드 |
|
파라미터화 | T 는 구체적이고 생성 가능한 타입입니다.
|
설명 |
구성 요소로부터 배열을 생성합니다.
참고: array<T,N>는 생성 가능한 타입입니다. 왜냐하면 요소 수가 생성자에 전달되는 인자 개수와 동일하므로, 셰이더 생성 시점에 완전히 결정됩니다. |
오버로드 |
|
파라미터화 | T 는 생성
가능한 타입입니다.
|
설명 |
구성 요소로부터 배열을 생성합니다.
구성 요소 타입은 요소 타입에서 유추됩니다. 배열의 크기는 요소의 개수에 의해 결정됩니다. |
17.1.2.2. bool
오버로드 |
|
파라미터화 | T 는 스칼라 타입입니다.
|
설명 |
bool 값을 생성합니다.
|
17.1.2.3. f16
오버로드 |
|
파라미터화 | T 는 스칼라 타입입니다.
|
설명 |
f16 값을 생성합니다.
|
17.1.2.4. f32
오버로드 |
|
파라미터화 | T 는 구체적 스칼라 타입입니다.
|
설명 |
f32 값을 생성합니다.
|
17.1.2.5. i32
오버로드 |
|
파라미터화 | T 는 스칼라 타입입니다.
|
설명 |
i32 값을 생성합니다.
|
17.1.2.6. mat2x2
오버로드 |
|
파라미터화 | T 는 f16 또는 f32S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
2x2 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 2x2 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
구성 요소로부터 2x2 컬럼-주 행렬 matrix를
생성합니다.
mat2x2(vec2(e1,e2), vec2(e3,e4))와 동일합니다. |
17.1.2.7. mat2x3
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
2x3 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 2x3 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
구성 요소로부터 2x3 컬럼-주 행렬 matrix를
생성합니다.
mat2x3(vec3(e1,e2,e3), vec3(e4,e5,e6))와 동일합니다. |
17.1.2.8. mat2x4
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
2x4 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 2x4 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
구성 요소로부터 2x4 컬럼-주 행렬 matrix를
생성합니다.
mat2x4(vec4(e1,e2,e3,e4), vec4(e5,e6,e7,e8))와 동일합니다. |
17.1.2.9. mat3x2
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
3x2 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 3x2 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
구성 요소로부터 3x2 컬럼-주 행렬 matrix를
생성합니다.
mat3x2(vec2(e1,e2), vec2(e3,e4), vec2(e5,e6))와 동일합니다. |
17.1.2.10.
mat3x3
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
3x3 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 3x3 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
구성 요소로부터 3x3 컬럼-주 행렬 matrix를
생성합니다.
mat3x3(vec3(e1,e2,e3), vec3(e4,e5,e6), vec3(e7,e8,e9))와 동일합니다. |
17.1.2.11.
mat3x4
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
3x4 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 3x4 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, 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
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
4x2 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 4x2 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
구성 요소로부터 4x2 컬럼-주 행렬 matrix를
생성합니다.
mat4x2(vec2(e1,e2), vec2(e3,e4), vec2(e5,e6), vec2(e7,e8))와 동일합니다. |
17.1.2.13.
mat4x3
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
4x3 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 4x3 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, 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
오버로드 |
|
파라미터화 | T 는 f16 또는 f32입니다.S 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 |
4x4 컬럼-주 행렬 matrix 생성자입니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f16, 또는 f32입니다.
|
설명 | 컬럼 벡터로부터 4x4 컬럼-주 행렬 matrix를 생성합니다. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, 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. 구조체
오버로드 |
|
파라미터화 | S 는 생성
가능한 구조체 타입이며, 멤버 타입은
T1 ... TN 입니다.
|
설명 | 멤버로부터 S 타입의 구조체를 생성합니다.
|
17.1.2.16. u32
오버로드 |
|
파라미터화 | T 는 스칼라 타입입니다.
|
설명 |
u32 값을 생성합니다.
|
참고: AbstractInt에서의 오버로드는
|
17.1.2.17. vec2
오버로드 |
|
파라미터화 | T 는 구체적 스칼라이고,S 는 스칼라입니다.
|
설명 | 두 컴포넌트 벡터를 e 로 모두
채워서 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 구체적 스칼라이고,S 는 스칼라입니다.
|
설명 |
컴포넌트별로 두 컴포넌트 벡터를 e.x 와
e.y 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 두 컴포넌트 벡터를 e1 과
e2 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractInt입니다.
|
설명 | vec2(0,0) 값을 반환합니다.
|
17.1.2.18. vec3
오버로드 |
|
파라미터화 | T 는 구체적 스칼라입니다.S 는 스칼라입니다.
|
설명 | 세 컴포넌트 벡터를 e 로 모두
채워서 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 구체적 스칼라입니다.S 는 스칼라입니다.
|
설명 |
컴포넌트별로 세 컴포넌트
벡터를 e.x ,
e.y , e.z 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 세 컴포넌트
벡터를 e1 ,
e2 , e3 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 세 컴포넌트
벡터를 v1.x ,
v1.y , e1 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 세 컴포넌트
벡터를 e1 ,
v1.x , v1.y 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractInt입니다.
|
설명 | vec3(0,0,0) 값을 반환합니다.
|
17.1.2.19. vec4
오버로드 |
|
파라미터화 | T 는 구체적 스칼라입니다.S 는 스칼라입니다.
|
설명 | 네 컴포넌트 벡터를 e 로 모두
채워서 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 구체적 스칼라입니다.S 는 스칼라입니다.
|
설명 |
컴포넌트별로 네 컴포넌트
벡터를 e.x ,
e.y , e.z , e.w 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 네 컴포넌트
벡터를 e1 ,
e2 , e3 , e4 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 네 컴포넌트
벡터를 e1 ,
v1.x , v1.y , e2 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 네 컴포넌트
벡터를 e1 ,
e2 , v1.x , v1.y 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 네 컴포넌트
벡터를 v1.x ,
v1.y , v2.x , v2.y 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 네 컴포넌트
벡터를 v1.x ,
v1.y , e1 , e2 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별로 네 컴포넌트
벡터를 v1.x ,
v1.y , v1.z , e1 로 생성합니다.
|
오버로드 |
|
파라미터화 | T 는 AbstractInt입니다.
|
설명 | vec4(0,0,0,0) 값을 반환합니다.
|
17.2. 비트 재해석 내장 함수
17.2.1. bitcast
bitcast
내장 함수는 한 타입의 값의 비트 표현을 다른 타입의 값으로 재해석할 때 사용됩니다.
내부 레이아웃 규칙은 § 14.4.4 값의 내부 레이아웃에 설명되어 있습니다.
오버로드 |
|
파라미터화 | T 는 구체적 수치 스칼라 또는 구체적 수치 벡터입니다.
|
설명 | 항등 변환. 컴포넌트별 변환은 T 가 벡터일 때
적용됩니다.결과는 e 입니다.
|
오버로드 |
|
파라미터화 | S 는 i32, u32, 또는 f32T 는 S 가 아니고 i32, u32, 또는 f32입니다.
|
설명 | 비트를 T 로 재해석.결과는 e 의 비트를 T 값으로 재해석한 것입니다.
|
오버로드 |
|
파라미터화 | S 는 i32, u32, 또는 f32T 는 S 가 아니고 i32, u32, 또는 f32입니다.
|
설명 | 컴포넌트별로 비트를
T 로 재해석.결과는 e 의 비트를 vecN<T> 값으로 재해석한 것입니다.
|
오버로드 |
|
파라미터화 | |
설명 |
e 가 u32로 표현 가능하면 항등 연산,
불가능하면 셰이더 생성 오류를 발생합니다.
즉, u32(e) 와 동일한 결과를 반환합니다.
|
오버로드 |
|
파라미터화 | T 는 i32, u32, 또는 f32입니다.
|
설명 | 컴포넌트별로 비트를
T 로 재해석.결과는 e 의 32비트를 T 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
|
오버로드 |
|
파라미터화 | T 는 i32, u32, 또는 f32입니다. |
설명 | 컴포넌트별로 비트를
T 로 재해석.결과는 e 의 64비트를 T 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
|
오버로드 |
|
파라미터화 | T 는 i32, u32, 또는 f32입니다.
|
설명 | 컴포넌트별로 비트를
f16으로 재해석. 결과는 e 의 32비트를 f16 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
|
오버로드 |
|
파라미터화 | T 는 i32, u32, 또는 f32입니다.
|
설명 | 컴포넌트별로
vec2<f16> 로 비트를 재해석.결과는 e 의 64비트를 f16 값으로 재해석한 것입니다. 내부 레이아웃 규칙을 따릅니다.
|
17.3. 논리 내장 함수
17.3.1. all
오버로드 |
|
설명 | e 의 모든 컴포넌트가 true일 때 true를 반환합니다.
|
오버로드 |
|
설명 | e 를 반환합니다.
|
17.3.2. any
오버로드 |
|
설명 | e 의 어떤 컴포넌트라도 true이면 true를 반환합니다.
|
오버로드 |
|
설명 | e 를 반환합니다.
|
17.3.3. select
오버로드 |
|
파라미터화 | T 는 스칼라 또는 벡터입니다.
|
설명 | cond 가 true일 때 t 를, 그렇지 않으면 f 를 반환합니다.
|
오버로드 |
|
파라미터화 | T 는 스칼라입니다.
|
설명 | 컴포넌트별 선택. 결과
컴포넌트 i 는 select(f[i], t[i], cond[i]) 로 평가됩니다.
|
17.4. 배열 내장 함수
17.4.1.
arrayLength
오버로드 |
|
파라미터화 | E 는 런타임
크기 배열의 요소 타입이며,액세스 모드 AM 은 읽기
또는 읽기/쓰기입니다.
|
설명 | NRuntime 값을 반환하며, 이는 런타임 크기 배열의 요소 개수입니다. |
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
오버로드 |
|
파라미터화 | S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e 의 절대값입니다.
컴포넌트 기준으로
T 가 벡터인 경우 적용됩니다.
|
17.5.2. acos
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e 의 역코사인(cos-1)의 주값(라디안)을 반환합니다.즉, cos (x ) = e 인 0 ≤ x ≤ π인 x 에
근사합니다.
컴포넌트 기준으로
|
스칼라 도메인 | [−1, 1] 구간 |
17.5.3. acosh
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
x 의 역쌍곡 코사인(cosh-1)을 쌍곡각으로 반환합니다.즉, cosh (a ) = x 인 0 ≤ a ≤ +∞인 a 에 근사합니다.
컴포넌트 기준으로
|
스칼라 도메인 | [1, +∞] 구간 |
17.5.4. asin
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e 의 역사인(sin-1)의 주값(라디안)을 반환합니다.즉, sin (x ) = e 인 -π/2 ≤ x ≤ π/2인
x 에 근사합니다.
컴포넌트 기준으로
|
스칼라 도메인 | [−1, 1] 구간 |
17.5.5. asinh
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
y 의 역쌍곡 사인(sinh-1)을 쌍곡각으로 반환합니다.즉, sinh (y ) = a 인 a 에 근사합니다.
컴포넌트 기준으로
|
17.5.6. atan
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e 의 역탄젠트(tan-1)의 주값(라디안)을 반환합니다.즉, tan (x ) = e 인 − π/2 ≤ x ≤ π/2인
x 에 근사합니다.
컴포넌트 기준으로
|
17.5.7. atanh
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
t 의 역쌍곡 탄젠트(tanh-1)를 쌍곡각으로 반환합니다.즉, tanh (a ) = t 인 a 에 근사합니다.
컴포넌트 기준으로
|
스칼라 도메인 | [−1, 1] 구간 |
17.5.8. atan2
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
y ÷x 의 탄젠트인 각도(라디안)를 [-π, π] 구간에서 반환합니다.
결과의 사분면은
참고: 결과의 오차는 제한되지 않습니다:
컴포넌트 기준으로
|
17.5.9. ceil
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e 의 천장값을 반환합니다.
컴포넌트 기준으로
T 가 벡터인 경우 적용됩니다.
|
17.5.10. clamp
오버로드 |
|
파라미터화 | S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e 의 값을 범위 내로 제한합니다.
컴포넌트 기준으로
|
17.5.11. cos
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e 의 코사인 값을 반환합니다. e 는 라디안 단위입니다.
컴포넌트 기준으로
T 가 벡터인 경우 적용됩니다.
|
스칼라 도메인 | 구간 (−∞, +∞) |
17.5.12. cosh
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
a 의 쌍곡 코사인 값을 반환합니다. a 는 쌍곡각입니다.
순수 수학 함수 (ea +
e−a)÷2에 근사하지만, 반드시 그렇게 계산되는 것은 아닙니다.
컴포넌트 기준으로
|
17.5.13.
countLeadingZeros
오버로드 |
|
파라미터화 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
|
설명 | 스칼라 타입일 때 e 의 최상위 비트부터 연속된 0 비트의 개수를 반환합니다.컴포넌트 기준으로 T 가 벡터인 경우 적용됩니다.일부 언어에서는 "clz"로도 알려져 있습니다. |
17.5.14.
countOneBits
오버로드 |
|
파라미터화 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
|
설명 | e 의 표현에서 1 비트의 개수를 반환합니다."population count"로도 알려져 있습니다. 컴포넌트 기준으로 T 가 벡터인 경우 적용됩니다.
|
17.5.15.
countTrailingZeros
오버로드 |
|
파라미터화 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
|
설명 | 스칼라 타입일 때 e 의 최하위 비트부터 연속된 0 비트의 개수를 반환합니다.컴포넌트 기준으로 T 가 벡터인 경우 적용됩니다.일부 언어에서는 "ctz"로도 알려져 있습니다. |
17.5.16. cross
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f32, 또는 f16입니다.
|
설명 | e1 과 e2 의 외적을 반환합니다.
|
도메인 |
선형 항으로부터 암시됨. 가능한 구현 예시:
|
17.5.17. degrees
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | 라디안을 도로 변환합니다. e1 × 180 ÷ π에 근사합니다.
컴포넌트 기준으로
T 가 벡터인 경우 적용됩니다.
|
17.5.18.
determinant
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f32, 또는 f16입니다.
|
설명 | e 의 행렬식 값을 반환합니다.
|
도메인 | 선형 항으로부터 암시됨. 행렬식의 표준 수학적 정의에 따릅니다. |
17.5.19. distance
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e1 과 e2 사이의 거리를 반환합니다 (예: length(e1 - e2) ).
도메인은 벡터
(e1,e2)의 뺄셈 e1−e2가 유효한 모든 경우입니다.
즉, |
17.5.20. dot
오버로드 |
|
파라미터화 | T 는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16입니다.
|
설명 | e1 과 e2 의 내적을 반환합니다.
|
도메인 | 선형 항으로부터 암시됨. 각 항 e1[i] × e2[i]의 합계에 해당합니다. |
17.5.21.
dot4U8Packed
오버로드 |
|
설명 | e1 과 e2 는 각각 네 개의 8비트 부호 없는 정수 컴포넌트로 이루어진 벡터로 해석됩니다.
이 두 벡터의 부호 없는 정수 내적을 반환합니다.
|
17.5.22.
dot4I8Packed
오버로드 |
|
설명 | e1 과 e2 는 각각 네 개의 8비트 부호 있는 정수 컴포넌트로 이루어진 벡터로 해석됩니다.
이 두 벡터의 부호 있는 정수 내적을 반환합니다. 각 컴포넌트는 곱셈을 수행하기 전에 i32로 부호 확장되며, 이후 덧셈 연산은 WGSL의 i32로 수행됩니다(덧셈 결과는
수학적으로 -65024에서 65536 범위 내에 있으므로 i32 범위 내에서 오버플로우가 발생하지 않습니다).
|
17.5.23. exp
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e1 의 자연 지수를 반환합니다(예: e e1 ).
컴포넌트 기준으로
T 가 벡터인 경우 적용됩니다.
|
17.5.24. exp2
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | 2를 e 의 거듭제곱한 값을 반환합니다(예: 2 e ).
컴포넌트 기준으로
T 가 벡터인 경우 적용됩니다.
|
17.5.25.
extractBits
(signed)
오버로드 |
|
파라미터화 | T 는 i32 또는 vecN<i32>입니다.
|
설명 |
정수에서 비트를 읽어오며, 부호 확장이 포함됩니다.
T 가 벡터인 경우 적용됩니다.
|
17.5.26.
extractBits
(unsigned)
오버로드 |
|
파라미터화 | T 는 u32 또는 vecN<u32>입니다.
|
설명 |
정수에서 비트를 읽어오며, 부호 확장은 없습니다.
T 가 벡터인 경우 적용됩니다.
|
17.5.27.
faceForward
오버로드 |
|
파라미터화 | T 는 vecN<AbstractFloat>, vecN<f32>, 또는 vecN<f16>입니다.
|
설명 | dot(e2, e3) 가 음수이면 e1 을, 그렇지 않으면 -e1 을 반환합니다.
|
도메인 | 도메인 제한은 dot(e2,e3) 연산에서 발생합니다: 선형 항으로부터 암시됨.
각 항 e2[i] × e3[i]의 합에 해당합니다.
|
17.5.28. firstLeadingBit
(signed)
오버로드 |
|
파라미터화 | T 는 i32 또는 vecN<i32>입니다.
|
설명 |
스칼라 T 일 때 결과는 다음과 같습니다:
컴포넌트 기준으로
|
참고: 부호 있는 정수는 2의 보수 표현을 사용하므로, 부호 비트가 가장 높은 비트 위치에 나타납니다. |
17.5.29. firstLeadingBit
(unsigned)
오버로드 |
|
파라미터화 | T 는 u32 또는 vecN<u32>입니다.
|
설명 |
스칼라 T 일 때 결과는 다음과 같습니다:
T 가 벡터인 경우 적용됩니다.
|
17.5.30.
firstTrailingBit
오버로드 |
|
파라미터화 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
|
설명 |
스칼라 T 일 때 결과는 다음과 같습니다:
T 가 벡터인 경우 적용됩니다.
|
17.5.31. floor
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e 의 내림값을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
17.5.32. fma
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e1 * e2 + e3 값을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
참고: 참고:
IEEE-754
|
도메인 | 선형 항에서 암시됨(식 e2 × e2 + e3). |
17.5.33. fract
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e 의 소수 부분(e - floor(e) )을 반환합니다.컴포넌트별로 T 가 벡터인 경우 적용됩니다.
|
참고: 유효 결과는 [0, 1.0] 구간에 있습니다.
예를 들어 |
17.5.34. frexp
오버로드 |
|
파라미터화 | T 는 f32입니다.
|
설명 |
e 를 소수 부분과 지수로 분리합니다.
참고: |
참고: |
오버로드 |
|
파라미터화 | T 는 f16입니다.
|
설명 |
e 를 소수 부분과 지수로 분리합니다.
참고: |
참고: |
오버로드 |
|
파라미터화 | T 는 AbstractFloat입니다.
|
설명 |
e 를 소수 부분과 지수로 분리합니다.
참고: AbstractFloat 연산에서 무한대 또는 NaN이 발생하면 셰이더 생성 오류가 발생합니다.
참고: |
참고: |
오버로드 |
|
파라미터화 | T 는 vecN<f32>입니다.
|
설명 |
e 의 각 요소(ei )를 소수 부분과 지수로 분리합니다.
참고: |
참고: |
오버로드 |
|
파라미터화 | T 는 vecN<f16>입니다.
|
설명 |
e 의 각 요소(ei )를 소수 부분과 지수로 분리합니다.
참고: |
참고: |
오버로드 |
|
파라미터화 | T 는 vecN<AbstractFloat>입니다.
|
설명 |
e 의 각 요소(ei )를 소수 부분과 지수로 분리합니다.
참고: AbstractFloat 연산에서 무한대 또는 NaN이 발생하면 셰이더 생성 오류가 발생합니다.
참고: |
참고:
|
17.5.35.
insertBits
오버로드 |
|
파라미터화 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
|
설명 |
정수의 비트를 설정합니다.
T 가 벡터인 경우 적용됩니다.
|
17.5.36.
inverseSqrt
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | sqrt(e) 의 역수를 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
스칼라 도메인 | 구간 [0, +∞] |
17.5.37. ldexp
오버로드 |
|
파라미터화 |
S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> I 는 AbstractInt, i32, vecN<AbstractInt>, 또는 vecN<i32>I 가 벡터일 때만 T 도 벡터T 가 추상
타입일 수 있는 경우는 I 도 추상 타입인 경우뿐이며 그 반대도 동일함
참고: 하나의 파라미터가 구체 타입이면 다른 파라미터도 자동 변환을 거쳐 구체 타입이 되고, 결과도 구체 타입이 됩니다. |
설명 |
e1 * 2 e2 를 반환합니다. 단, 예외:
여기서 bias는 부동소수점 포맷의 지수 바이어스입니다:
x = ldexp(frexp(x).fract, frexp(x).exp) 컴포넌트별로
참고: |
17.5.38. length
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 |
e 의 길이를 반환합니다.T 가 스칼라일 경우 절댓값으로
평가됩니다.T 가 벡터 타입일 경우 sqrt(e[0] 2
+ e[1] 2 + ...) 로 평가됩니다.
참고: 스칼라의 경우 |
17.5.39. log
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e 의 자연 로그 값을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
스칼라 도메인 | 구간 [0, +∞] |
17.5.40. log2
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S>입니다. |
설명 | e 의 2진 로그 값을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
스칼라 도메인 | 구간 [0, +∞] |
17.5.41.
max
오버로드 |
|
파라미터화 | S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 |
e1 이 e2 보다 작으면 e2 를 반환하고, 그렇지 않으면 e1 을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
17.5.42.
min
오버로드 |
|
파라미터화 | S는 AbstractInt, AbstractFloat, i32, u32, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 |
e2 가 e1 보다 작으면 e2 를, 그렇지 않으면 e1 을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
17.5.43. mix
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | e1 과 e2 를 선형적으로 혼합한 값을 반환합니다(예:
e1 * (T(1) - e3) + e2 * e3 ).
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
도메인 | 선형 항에서 암시됨: e1[i] × (1 − e3[i]) + e2[i] × e3[i]. e2[i] × e2[i] + e3[i]. |
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f32, 또는 f16T2 는 vecN<T>
|
설명 | e1 과 e2 를 컴포넌트별로 선형적으로 혼합하며, 각 컴포넌트에 대해 스칼라 혼합 인자 e3 를
적용합니다.mix(e1, e2, T2(e3)) 와 동일합니다.
|
도메인 | 선형 항에서 암시됨: e1[i] × (1 − e3) + e2[i] × e3. |
17.5.44. modf
오버로드 |
|
파라미터화 | T 는 f32
|
설명 |
e 를 소수 부분과 정수 부분으로 분리합니다.
정수 부분은 trunc(
|
참고: |
오버로드 |
|
파라미터화 | T 는 f16
|
설명 |
e 를 소수 부분과 정수 부분으로 분리합니다.
정수 부분은 trunc(
|
참고: |
오버로드 |
|
파라미터화 | T 는 AbstractFloat
|
설명 |
e 를 소수 부분과 정수 부분으로 분리합니다.
정수 부분은 trunc(
|
참고: |
오버로드 |
|
파라미터화 | T 는 vecN<f32>
|
설명 |
e 의 각 컴포넌트를 소수 부분과 정수 부분으로 분리합니다.
각
|
참고: |
오버로드 |
|
파라미터화 | T 는 vecN<f16>
|
설명 |
e 의 각 컴포넌트를 소수 부분과 정수 부분으로 분리합니다.
각
|
참고: |
오버로드 |
|
파라미터화 | T 는 vecN<AbstractFloat>
|
설명 |
e 의 각 컴포넌트를 소수 부분과 정수 부분으로 분리합니다.
각
|
참고:
|
17.5.45.
normalize
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f32, 또는 f16입니다.
|
설명 |
e 와 같은 방향의 단위 벡터를 반환합니다.
도메인은 영벡터를 제외한 모든 벡터입니다. |
17.5.46. pow
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | e1 을 e2 의 거듭제곱한 값을 반환합니다.
컴포넌트별로
T 가 벡터인 경우 적용됩니다.
|
스칼라 도메인 |
확장 실수
(x,y) 쌍 전체 집합에서, 다음을 제외합니다:
이 규칙은 결과가 |
17.5.47.
quantizeToF16
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>입니다.
|
설명 |
32비트 부동소수점 값 e 를 IEEE-754 binary16 값으로 변환한 뒤,
다시 IEEE-754 binary32 값으로 변환한 것과 같이 양자화합니다.
중간 binary16 값이 0으로 플러시될 수 있습니다. 즉, 중간 binary16 값이 서브노멀일 경우 최종 결과가 0이 될 수 있습니다. § 15.7.6 부동소수점 변환을 참고하십시오. 컴포넌트별로
|
참고: vec2<f32>의 경우
|
17.5.48. radians
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | 도를 라디안으로 변환합니다. e1 × π ÷ 180에 근사합니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
17.5.49. reflect
오버로드 |
|
파라미터화 | T 는 vecN<AbstractFloat>, vecN<f32>, 또는 vecN<f16>입니다.
|
설명 | 입사 벡터 e1 와 표면 방향 e2 에 대해,
반사 방향 e1 - 2 * dot(e2, e1) * e2 를 반환합니다.
|
17.5.50. refract
오버로드 |
|
파라미터화 | 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
오버로드 |
|
파라미터화 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다.
|
설명 | e 의 비트를 뒤집습니다: 결과의 k 번째 비트는 e 의 31 -k 번째 비트와
같습니다.컴포넌트별로 T 가 벡터일 때 적용됩니다.
|
17.5.52. round
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | 결과는 e 와 가장 가까운 정수 k 를 부동소수점 값으로 반환합니다.e 가 정수 k 와 k + 1 사이에 정확히 위치할 경우,
k 가 짝수면 k 를, 홀수면 k + 1 을 반환합니다.컴포넌트별로 T 가 벡터일 때 적용됩니다.
|
17.5.53.
saturate
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | clamp(e, 0.0, 1.0) 을 반환합니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
17.5.54. sign
오버로드 |
|
파라미터화 | S는 AbstractInt, AbstractFloat, i32, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 |
결과는 다음과 같습니다:
컴포넌트별로
|
17.5.55. sin
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | e 의 사인 값을 반환합니다. e 는 라디안 단위입니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
스칼라 도메인 | 구간 (−∞, +∞) |
17.5.56. sinh
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 |
a 의 쌍곡 사인 값을 반환합니다. a 는 쌍곡각입니다.
순수 수학 함수
(ea − e−a)÷2에 근사하지만, 반드시 그렇게 계산되는 것은 아닙니다.
컴포넌트별로
|
17.5.57.
smoothstep
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 |
0과 1 사이의 스무스 Hermite 보간 값을 반환합니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
스칼라 질적으로 설명하면:
|
17.5.58. sqrt
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | e 의 제곱근 값을 반환합니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
스칼라 도메인 | 구간 [0, +∞] |
17.5.59. step
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | edge ≤ x 이면 1.0을, 그렇지 않으면 0.0을 반환합니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
17.5.60. tan
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | e 의 탄젠트 값을 반환합니다. e 는 라디안 단위입니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
스칼라 도메인 | 구간 (−∞, +∞) |
17.5.61. tanh
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 |
a 의 쌍곡 탄젠트 값을 반환합니다. a 는 쌍곡각입니다.
순수 수학 함수
(ea − e−a) ÷ (ea +
e−a)
에 근사하지만, 반드시 그렇게 계산되는 것은 아닙니다.
컴포넌트별로
|
17.5.62.
transpose
오버로드 |
|
파라미터화 | T 는 AbstractFloat, f32, 또는 f16
|
설명 | e 의 전치 행렬을 반환합니다.
|
17.5.63. trunc
오버로드 |
|
파라미터화 | S는 AbstractFloat, f32, 또는 f16 T는 S 또는 vecN<S> |
설명 | truncate(e ) 값을
반환합니다. 이는 e 의 절댓값보다 작거나 같은 가장 가까운 정수입니다.
컴포넌트별로
T 가 벡터일 때 적용됩니다.
|
17.6. 미분 내장 함수
§ 15.6.2 미분을 참고하세요.
이 함수들의 호출은 다음을 의미합니다:
-
트리거된 경우 derivative_uniformity 진단을 발생시키며, uniformity analysis가 호출이 uniform control flow 내에 있다고 증명하지 못할 경우에 해당합니다.
17.6.1. dpdx
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
윈도우 x 좌표에 대한 e 의 편미분입니다.
결과는 dpdxFine(e) 또는 dpdxCoarse(e) 와 동일합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.2. dpdxCoarse
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
윈도우 x 좌표에 대한 e 의 편미분을 로컬 차분을 사용하여 반환합니다.
이는 dpdxFine(e) 보다 더 적은 고유 위치를 반환할 수 있습니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.3. dpdxFine
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
윈도우 x 좌표에 대한 e 의 편미분을 반환합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.4. dpdy
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
e 의 윈도우 y 좌표에 대한 편미분입니다.
결과는 dpdyFine(e) 또는 dpdyCoarse(e) 와 동일합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.5. dpdyCoarse
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
e 의 윈도우 y 좌표에 대한 편미분을 로컬 차분을 사용하여 반환합니다.
이는 dpdyFine(e) 보다 더 적은 고유 위치를 반환할 수 있습니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.6. dpdyFine
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
e 의 윈도우 y 좌표에 대한 편미분을 반환합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.7. fwidth
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
abs(dpdx(e)) + abs(dpdy(e)) 를 반환합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.8.
fwidthCoarse
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
abs(dpdxCoarse(e)) + abs(dpdyCoarse(e)) 를 반환합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.6.9. fwidthFine
오버로드 |
|
파라미터화 | T 는 f32 또는 vecN<f32>
|
설명 |
abs(dpdxFine(e)) + abs(dpdyFine(e)) 를 반환합니다.
uniform control flow가 아닌 곳에서 호출 시 불확정 값을 반환합니다. |
17.7. 텍스처 내장 함수
파라미터 값은 각 텍스처 타입에 대해 유효해야 합니다.
17.7.1. textureDimensions
텍스처 또는 텍스처의 mip 레벨의 크기를 텍셀 단위로 반환합니다.
파라미터화 | 오버로드 |
---|---|
ST는 i32, u32, 또는 f32 F는 텍셀 포맷 A는 액세스 모드 T는 texture_1d<ST> 또는 texture_storage_1d<F,A>
|
|
ST는 i32, u32, 또는 f32 T는 texture_1d<ST>
|
|
ST는 i32, u32, 또는 f32 F는 텍셀 포맷 A는 액세스 모드 T는 texture_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
|
|
ST는 i32, u32, 또는 f32 T는 texture_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
|
|
ST는 i32, u32, 또는 f32 F는 텍셀 포맷 A는 액세스 모드 T는 texture_3d<ST> 또는 texture_storage_3d<F,A>
|
|
ST는 i32, u32, 또는 f32 T는 texture_3d<ST>
|
|
파라미터:
t
| 샘플링된, 멀티샘플링된, 깊이, 스토리지, 또는 외부 텍스처. |
level
|
mip 레벨이며, level 0은 텍스처의 전체
크기 버전을 포함합니다. 생략하면 level 0의 크기가 반환됩니다. |
반환:
텍스처의 좌표 크기입니다.
즉, 결과는 논리 텍셀 주소의 좌표에 대한 정수 경계를 제공합니다. mip 레벨 수, 배열 크기, 샘플 수는 제외됩니다.
큐브 기반 텍스처의 경우 결과는 각 큐브 면의 크기입니다. 큐브 면은 정사각형이므로 결과의 x와 y 성분이 동일합니다.
level
이 [0, textureNumLevels(t))
범위를 벗어나면 반환 타입에 대해 불확정 값
이 반환될 수 있습니다.
17.7.2. textureGather
텍스처 갤러 연산은 2D, 2D 배열, 큐브 또는 큐브 배열 텍스처에서 읽기를 수행하며, 다음과 같이 네 성분 벡터를 계산합니다:
-
선형 필터링으로 샘플링 연산을 할 때 사용할 네 개의 텍셀을 mip 레벨 0에서 찾습니다:
-
지정된 좌표, 배열 인덱스(존재하는 경우), 오프셋(존재하는 경우)을 사용합니다.
-
텍셀들은 텍스처 공간 좌표(u,v)로 볼 때 인접해 있으며, 정사각형을 이룹니다.
-
텍스처의 경계, 큐브 면 경계, 큐브 코너에서 선택된 텍셀은 일반 텍스처 샘플링과 동일하게 처리됩니다.
-
-
각 텍셀에서 하나의 채널을 읽고 스칼라 값으로 변환합니다.
-
비-깊이 텍스처의 경우, 0부터 시작하는
component
파라미터가 사용할 채널을 지정합니다.-
텍스처 포맷이 지정된 채널을 지원한다면, 즉
component
보다 많은 채널이 있다면:-
텍셀 값이
v
일 때 스칼라 값v[component]
를 반환합니다.
-
-
그렇지 않은 경우:
-
component
가 1 또는 2이면 0.0을 반환합니다. -
component
가 3(alpha 채널)이면 1.0을 반환합니다.
-
-
-
깊이 텍스처의 경우 텍셀 값을 반환합니다. (깊이 텍스처는 채널이 하나입니다.)
-
-
이전 단계에서 생성된 스칼라들을 텍셀의 상대 좌표에 따라 아래와 같이 네 성분 벡터로 배치하여 반환합니다:
-
결과 성분 상대 텍셀 좌표 x (umin,vmax) y (umax,vmax) z (umax,vmin) w (umin,vmin)
-
이 네 개의 텍셀은 WebGPU 샘플러 디스크립터에서 설명된 샘플링 영역을 구성합니다.
파라미터화 | 오버로드 |
---|---|
C는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 A는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 A는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 A는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
| |
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
파라미터:
component
|
깊이 텍스처가 아닌 경우에만 적용됩니다.
선택된 텍셀에서 읽을 채널의 인덱스입니다. 제공되는 경우 component 표현식은 상수 표현식이어야 합니다(예: 1 ).값은 최소 0, 최대 3이어야 합니다. 이 범위를 벗어나면 셰이더 생성 오류가 발생합니다. |
t
| 샘플링된 또는 깊이 텍스처에서 읽어옵니다. |
s
| 샘플러 타입입니다. |
coords
| 텍스처 좌표입니다. |
array_index
|
0부터 시작하는 텍스처 배열 인덱스. 이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다. |
offset
|
샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다.
이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.offset 표현식은 상수 표현식이어야 합니다(예:
vec2<i32>(1, 2) ).각 offset 성분은 최소 -8 , 최대 7 이어야 합니다. 범위를 벗어나면 셰이더
생성 오류가 발생합니다.
|
반환:
위에서 설명한 대로 선택된 텍셀의 지정된 채널에서 추출한 성분으로 구성된 네 성분 벡터를 반환합니다.
@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
텍스처 갤러 비교 연산은 깊이 텍스처의 네 텍셀에 대해 깊이 비교를 수행하고, 결과를 하나의 벡터에 모읍니다. 방식은 다음과 같습니다:
-
선형 필터링으로 깊이 샘플링 연산을 할 때 사용할 네 개의 텍셀을 mip 레벨 0에서 찾습니다:
-
지정된 좌표, 배열 인덱스(존재하는 경우), 오프셋(존재하는 경우)을 사용합니다.
-
텍셀들은 텍스처 공간 좌표(u,v)로 볼 때 인접해 있으며, 정사각형을 이룹니다.
-
텍스처의 경계, 큐브 면 경계, 큐브 코너에서 선택된 텍셀은 일반 텍스처 샘플링과 동일하게 처리됩니다.
-
-
각 텍셀에 대해 깊이 참조값과 비교를 수행하며, 비교 샘플러 파라미터에 따라 0.0 또는 1.0 값을 얻습니다.
-
아래 상대 텍셀 좌표에 따라 비교 결과를 네 성분 벡터로 배치하여 반환합니다:
-
결과 성분 상대 텍셀 좌표 x (umin,vmax) y (umax,vmax) z (umax,vmin) w (umin,vmin)
-
파라미터화 | 오버로드 |
---|---|
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
| |
A는 i32 또는 u32 |
|
파라미터:
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
샘플링이나 필터링 없이 텍스처에서 단일 텍셀을 읽어옵니다.
파라미터화 | 오버로드 |
---|---|
C는 i32 또는 u32 L는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 L는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 A는 i32 또는 u32 L는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 L는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 S는 i32 또는 u32 ST는 i32, u32, 또는 f32 |
|
C는 i32 또는 u32 L는 i32 또는 u32 |
|
C는 i32 또는 u32 A는 i32 또는 u32 L는 i32 또는 u32 |
|
C는 i32 또는 u32 S는 i32 또는 u32 |
|
C는 i32 또는 u32 |
|
C는 i32 또는 u32 AM는 read 또는 read_write CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요. |
|
C는 i32 또는 u32 AM는 read 또는 read_write CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요. |
|
C는 i32 또는 u32 AM는 read 또는 read_write A는 i32 또는 u32 CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요. |
|
C는 i32 또는 u32 AM는 read 또는 read_write CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 텍셀 포맷과 채널 포맷의 매핑을 확인하세요. |
|
파라미터:
t
| 샘플링된, 멀티샘플링된, 깊이, 스토리지, 또는 외부 텍스처 |
coords
| 0부터 시작하는 텍셀 좌표 |
array_index
| 0부터 시작하는 텍스처 배열 인덱스 |
level
| mip 레벨이며, level 0은 텍스처의 전체 크기 버전을 포함합니다. |
sample_index
| 멀티샘플링 텍스처의 0부터 시작하는 샘플 인덱스 |
반환:
필터링되지 않은 텍셀 데이터입니다.
논리 텍셀 주소가 다음 중 하나에 해당하면 유효하지 않습니다:
-
coords
의 어느 요소라도 해당 요소에 대해[0, textureDimensions(t, level))
범위를 벗어나면 -
array_index
가[0, textureNumLayers(t))
범위를 벗어나면 -
level
이[0, textureNumLevels(t))
범위를 벗어나면 -
sample_index
가[0, textureNumSamples(s))
범위를 벗어나면
논리 텍셀 주소가 유효하지 않을 경우, 내장 함수는 다음 중 하나를 반환합니다:
-
텍스처 경계 내의 어떤 텍셀의 데이터
-
비-깊이 텍스처의 경우 적합한 타입의 벡터 (0,0,0,0) 또는 (0,0,0,1)
-
깊이 텍스처의 경우 0.0
17.7.5. textureNumLayers
배열형 텍스처의 레이어(요소) 수를 반환합니다.
파라미터화 | 오버로드 |
---|---|
F는 텍셀
포맷 A는 액세스 모드 ST는 i32, u32, 또는 f32 T는 texture_2d_array<ST> , texture_cube_array<ST> ,
texture_depth_2d_array , texture_depth_cube_array ,
또는 texture_storage_2d_array<F,A>
|
|
파라미터:
t
| 샘플링된, 깊이, 또는 스토리지 배열 텍스처 |
반환:
텍스처가 큐브 기반일 때는 큐브 배열 텍스처의 큐브 개수를 반환합니다.
그 외에는 배열형 텍스처의 레이어 수(텍셀의 동질 격자수)를 반환합니다.
17.7.6. textureNumLevels
텍스처의 mip 레벨 수를 반환합니다.
파라미터화 | 오버로드 |
---|---|
ST는 i32, u32, 또는 f32 T는 texture_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
|
|
파라미터:
t
| 샘플링된 또는 깊이 텍스처 |
반환:
텍스처의 mip 레벨 개수를 반환합니다.
17.7.7. textureNumSamples
멀티샘플링 텍스처에서 텍셀당 샘플 수를 반환합니다.
파라미터화 | 오버로드 |
---|---|
ST는 i32, u32, 또는 f32 T는 texture_multisampled_2d<ST>
또는 texture_depth_multisampled_2d
|
|
파라미터:
t
| 멀티샘플링 텍스처 |
반환:
17.7.8. textureSample
텍스처를 샘플링합니다.
반드시 fragment 셰이더 단계에서만 사용해야 합니다.
균일성 분석이 이 함수 호출이 uniform control flow 내에 있음을 증명하지 못할 경우, derivative_uniformity 진단이 트리거됩니다.
파라미터화 | 오버로드 |
---|---|
| |
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
T는 texture_3d<f32> 또는 texture_cube<f32>
|
|
| |
A는 i32 또는 u32 |
|
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
| |
A는 i32 또는 u32 |
|
파라미터:
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 진단이 트리거됩니다.
파라미터화 | 오버로드 |
---|---|
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
T는 texture_3d<f32> 또는 texture_cube<f32>
|
|
| |
A는 i32 또는 u32 |
|
파라미터:
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 진단이 트리거됩니다.
파라미터화 | 오버로드 |
---|---|
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
| |
A는 i32 또는 u32 |
|
파라미터:
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
깊이 텍스처를 샘플링하고, 샘플링된 깊이 값을 참조값과 비교합니다.
파라미터화 | 오버로드 |
---|---|
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
| |
A는 i32 또는 u32 |
|
파라미터:
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
와 유사하지만, 다음 사항이 다릅니다:
-
textureSampleCompareLevel
은 항상 mip 레벨 0에서 텍셀을 샘플링합니다.-
이 함수는 도함수를 계산하지 않습니다.
-
textureSampleCompareLevel
은 uniform control flow 내에서 호출될 필요가 없습니다.
-
-
textureSampleCompareLevel
은 모든 셰이더 단계에서 호출할 수 있습니다.
17.7.12.
textureSampleGrad
명시적으로 지정한 그래디언트를 사용하여 텍스처를 샘플링합니다.
파라미터화 | 오버로드 |
---|---|
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
T는 texture_3d<f32> 또는 texture_cube<f32>
|
|
| |
A는 i32 또는 u32 |
|
파라미터:
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 레벨을 사용하여 텍스처를 샘플링합니다.
파라미터화 | 오버로드 |
---|---|
| |
| |
| |
A는 i32 또는 u32 |
|
A는 i32 또는 u32 |
|
T는 texture_3d<f32> 또는 texture_cube<f32>
|
|
| |
A는 i32 또는 u32 |
|
L는 i32 또는 u32 |
|
L는 i32 또는 u32 |
|
A는 i32 또는 u32 L는 i32 또는 u32 |
|
A는 i32 또는 u32 L는 i32 또는 u32 |
|
L는 i32 또는 u32 |
|
A는 i32 또는 u32 L는 i32 또는 u32 |
|
파라미터:
t
| 샘플링할 샘플링 또는 깊이 텍스처 |
s
| 샘플러 타입 |
coords
| 샘플링에 사용되는 텍스처 좌표 |
array_index
|
샘플링할 0부터 시작하는 텍스처 배열 인덱스. 이 값은 [0, textureNumLayers(t) - 1] 범위로 클램핑됩니다. |
level
|
mip 레벨입니다. level 0은 텍스처의 전체 크기 버전을 포함합니다.
level 이 f32 인 함수에서는, 값이 소수일 경우 Texture Format Capabilities에
따라 포맷이 필터링 가능하면 두 레벨 사이를 보간할 수 있습니다.
|
offset
|
샘플링 전에 비정규화된 텍스처 좌표에 적용하는 선택적 텍셀 오프셋입니다.
이 오프셋은 텍스처 래핑 모드를 적용하기 전에 적용됩니다.offset 표현식은 상수 표현식이어야 합니다(예:
vec2<i32>(1, 2) ).각 offset 성분은 최소 -8 , 최대 7 이어야 합니다. 범위를 벗어나면 셰이더
생성 오류가 발생합니다.
|
반환:
샘플링된 값입니다.
17.7.14.
textureSampleBaseClampToEdge
텍스처 뷰의 베이스 레벨에서 샘플링하며, 텍스처 좌표는 아래와 같이 엣지에 클램핑됩니다.
파라미터화 | 오버로드 |
---|---|
T는 texture_2d<f32> 또는 texture_external
|
|
파라미터:
t
| 샘플링할 샘플링 또는 외부 텍스처 |
s
| 샘플러 타입 |
coords
|
샘플링에 사용되는 텍스처 좌표
샘플링 전에 지정한 좌표는 다음 사각형으로 클램핑됩니다.
여기서
Note: half-texel 보정은 샘플러의 |
반환:
샘플링된 값입니다.
17.7.15. textureStore
단일 텍셀을 텍스처에 기록합니다.
파라미터화 | 오버로드 |
---|---|
F는 텍셀
포맷 C는 i32 또는 u32 AM는 write 또는 read_write CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요. |
|
F는 텍셀
포맷 C는 i32 또는 u32 AM는 write 또는 read_write CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요. |
|
F는 텍셀
포맷 C는 i32 또는 u32 AM는 write 또는 read_write A는 i32 또는 u32 CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요. |
|
F는 텍셀
포맷 C는 i32 또는 u32 AM는 write 또는 read_write CF는 저장 텍셀 포맷 F에 따라 달라집니다. 텍셀 포맷 표에서 포맷과 채널 포맷의 매핑을 확인하세요. |
|
파라미터:
t
| write-only storage 텍스처 또는 read-write storage 텍스처 |
coords
|
0부터 시작하는 텍셀 좌표 |
array_index
| 0부터 시작하는 텍스처 배열 인덱스 |
value
|
새 텍셀 값입니다.
value 는 역 채널 전송 함수를 사용해 변환됩니다.
|
Note:
논리 텍셀 주소가 다음 중 하나에 해당하면 유효하지 않습니다:
-
coords
의 어느 요소라도 해당 요소에 대해[0, textureDimensions(t))
범위를 벗어나면 -
array_index
가[0, textureNumLayers(t))
범위를 벗어나면
논리 텍셀 주소가 유효하지 않은 경우, 내장 함수는 실행되지 않습니다.
17.8. 원자 내장 함수
원자 내장 함수는 원자 객체를 읽기/쓰기/읽기-수정-쓰기 하는 데 사용할 수 있습니다. § 6.2.8 원자 타입에서 허용된 유일한 연산입니다.
모든 원자 내장 함수는 relaxed
메모리 순서를 사용합니다. 이는 동기화와 순서 보장이 동일한 메모리 위치에서의 원자 연산들 사이에만
적용된다는 의미입니다. 원자와 비원자 접근, 또는 서로 다른 메모리 위치에 대한 원자 접근 사이에는 동기화나 순서 보장이 없습니다.
원자 내장 함수는 절대 정점 셰이더 단계에서 사용되어서는 안 됩니다.
모든 원자 내장 함수에서 atomic_ptr
파라미터의 주소 공간 AS
는 반드시 storage 또는 workgroup이어야 합니다.
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. 원자 읽기-수정-쓰기 산술 및 논리 함수
각 함수는 다음 단계를 원자적으로 수행합니다:
-
atomic_ptr
가 가리키는 원래 값을 읽습니다. -
함수 이름에 따른 연산(예: max)을 값 v로 수행하여 새 값을 얻습니다.
-
새 값을
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 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.
// 모든 연산은 원자적으로 수행됨 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 연산을 원자적으로 수행하며, 연산 이전의 원래 값을 반환합니다.
// 모든 연산은 원자적으로 수행됨 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 연산을 원자적으로 수행하고, 연산 이전에 원자 객체에 저장되어 있던 원래
값을 반환합니다.
// 모든 연산은 원자적으로 수행됩니다 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
타입으로
값을 명시적으로 선언할 수는 없지만, 값의 타입이 추론될 수 있습니다.
다음 단계들이 원자적으로 수행됩니다:
-
atomic_ptr
가 가리키는 원래 값을 로드합니다. -
원래 값과
cmp
값을 동등 비교 연산으로 비교합니다. -
동등 비교 연산 결과가
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
오버로드 |
|
설명 |
네 개의 정규화된 부동소수점 값을 8비트 부호 있는 정수로 변환한 후,
이를 하나의 u32 값으로 결합합니다.
입력의 |
17.9.2.
pack4x8unorm
오버로드 |
|
설명 |
네 개의 정규화된 부동소수점 값을 8비트 부호 없는 정수로 변환한 후,
이를 하나의 u32 값으로 결합합니다.
입력의 |
17.9.3. pack4xI8
오버로드 |
|
설명 |
e 의 각 컴포넌트의 하위 8비트만 u32
값에 패킹하고, 사용되지 않는 비트는 모두 버립니다.
입력의 |
17.9.4. pack4xU8
오버로드 |
|
설명 |
e 의 각 컴포넌트의 하위 8비트만 u32
값에 패킹하고, 사용되지 않는 비트는 모두 버립니다.
입력의 |
17.9.5.
pack4xI8Clamp
오버로드 |
|
설명 |
e 의 각 컴포넌트를 [-128, 127] 범위로 클램프한 후, 각 컴포넌트의 하위 8비트를
u32 값에 패킹합니다.
입력의 |
17.9.6.
pack4xU8Clamp
오버로드 |
|
설명 |
e 의 각 컴포넌트를 [0, 255] 범위로 클램프한 후, 각 컴포넌트의 하위 8비트를
u32 값에 패킹합니다.
입력의 |
17.9.7.
pack2x16snorm
오버로드 |
|
설명 | 두 개의 정규화된 부동소수점 값을 16비트 부호 있는 정수로 변환한 후,
이를 하나의 u32 값으로 결합합니다.입력의 e[i] 컴포넌트는 16비트 2의 보수 정수 값
⌊ 0.5 + 32767 × min(1, max(-1, e[i])) ⌋로 변환되며, 이 값은 결과의
16 × i 부터
16 × i + 15 비트에 배치됩니다.
|
17.9.8.
pack2x16unorm
오버로드 |
|
설명 | 두 개의 정규화된 부동소수점 값을 16비트 부호 없는 정수로 변환한 후,
이를 하나의 u32 값으로 결합합니다.입력의 e[i] 컴포넌트는 16비트 부호 없는 정수 값
⌊ 0.5 + 65535 × min(1, max(0, e[i])) ⌋로 변환되며, 이 값은 결과의
16 × i 부터
16 × i + 15 비트에 배치됩니다.
|
17.9.9.
pack2x16float
오버로드 |
|
설명 |
두 개의 부동소수점 값을 half-precision 부동소수점 값으로 변환한 후,
이를 하나의 u32 값으로 결합합니다.입력의 e[i] 컴포넌트는 IEEE-754 binary16
값으로 변환되며,
이 값은 결과의
16 × i 부터
16 × i + 15 비트에 배치됩니다.
§ 15.7.6 부동소수점 변환을 참고하세요.
만약
|
17.10. 데이터 언패킹 내장 함수
데이터 언패킹 내장 함수는 WGSL 타입과 직접적으로 일치하지 않는 데이터 형식의 값을 디코딩하는 데 사용할 수 있습니다. 이를 통해 프로그램은 메모리에서 많은 밀집된 값을 읽을 수 있으며, 셰이더의 메모리 대역폭 요구를 줄일 수 있습니다.
각 내장 함수는 입력 값을 채널로 분리한 다음, 각 채널에 채널 전달 함수를 적용합니다.
참고: unorm 값을 언패킹할 때, 정규화된 부동소수점 결과는 [0.0, 1.0] 구간에 있습니다.
참고: snorm 값을 언패킹할 때, 정규화된 부동소수점 결과는 [-1.0, 1.0] 구간에 있습니다.
17.10.1.
unpack4x8snorm
오버로드 |
|
설명 | 32비트 값을 네 개의 8비트 조각으로 분해한 뒤,
각 조각을 부호 있는 정규화된 부동소수점 값으로 재해석합니다. 결과의 i 컴포넌트는 max(v ÷ 127, -1)이며, v 는
e 의 8×i 부터 8×i + 7 비트까지를 2의 보수 부호 있는 정수로 해석한 값입니다.
|
17.10.2.
unpack4x8unorm
오버로드 |
|
설명 | 32비트 값을 네 개의 8비트 조각으로 분해한 뒤,
각 조각을 부호 없는 정규화된 부동소수점 값으로 재해석합니다. 결과의 i 컴포넌트는 v ÷ 255이며, v 는
e 의 8×i 부터 8×i + 7 비트까지를 부호 없는 정수로 해석한 값입니다.
|
17.10.3.
unpack4xI8
오버로드 |
|
설명 | e 는 네 개의 8비트 부호 있는 정수 컴포넌트로 이루어진 벡터로 해석됩니다. e 를 부호 확장하여 vec4<i32>로
언패킹합니다.
|
17.10.4.
unpack4xU8
오버로드 |
|
설명 | e 는 네 개의 8비트 부호 없는 정수 컴포넌트로 이루어진 벡터로 해석됩니다. e 를 0 확장하여 vec4<u32>로
언패킹합니다.
|
17.10.5.
unpack2x16snorm
오버로드 |
|
설명 | 32비트 값을 두 개의 16비트 조각으로 분해한 뒤,
각 조각을 부호 있는 정규화된 부동소수점 값으로 재해석합니다. 결과의 i 컴포넌트는 max(v ÷ 32767, -1)이며, v 는
e 의 16×i 부터 16×i + 15 비트까지를 2의 보수 부호 있는 정수로 해석한 값입니다.
|
17.10.6.
unpack2x16unorm
오버로드 |
|
설명 | 32비트 값을 두 개의 16비트 조각으로 분해한 뒤,
각 조각을 부호 없는 정규화된 부동소수점 값으로 재해석합니다. 결과의 i 컴포넌트는 v ÷ 65535이며, v 는
e 의 16×i 부터 16×i + 15 비트까지를 부호 없는 정수로 해석한 값입니다.
|
17.10.7.
unpack2x16float
오버로드 |
|
설명 | 32비트 값을 두 개의 16비트 조각으로 분해한 뒤, 각 조각을 부동소수점 값으로 재해석합니다. 결과의 i 컴포넌트는 v 의 f32 표현이며,
v 는 e 의 16×i 부터 16×i + 15 비트까지를
IEEE-754 binary16 값으로 해석한 것입니다.
§ 15.7.6 부동소수점 변환을 참고하세요.
|
17.11. 동기화 내장 함수
모든 동기화 함수는 Acquire/Release 메모리 순서를 가진 제어 배리어를 실행합니다. 즉, 모든 동기화 함수와 영향받는 메모리 및 원자 연산들은 동기화 함수와 관련하여 프로그램 순서로 정렬됩니다. 또한, 동기화 함수 이전에 프로그램 순서로 배치된 영향받는 메모리와 원자 연산은 해당 워크그룹 내의 모든 다른 스레드에게 보여진 후, 동기화 함수 이후에 프로그램 순서로 배치된 영향받는 메모리 또는 원자 연산이 워크그룹 멤버에 의해 실행될 수 있습니다.
모든 동기화 함수는 Workgroup
메모리
범위를 사용합니다.
모든 동기화 함수는 Workgroup
실행 범위를 가집니다.
모든 동기화 함수는 컴퓨트 셰이더 스테이지에서만 사용되어야
합니다.
모든 동기화 함수는 균일 제어
흐름 내에서만 호출되어야
합니다.
17.11.1.
storageBarrier
오버로드 |
|
설명 | storage 주소 공간의 메모리 및 원자 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다. |
17.11.2.
textureBarrier
오버로드 |
|
설명 | handle 주소 공간의 메모리 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다. |
17.11.3.
workgroupBarrier
오버로드 |
|
설명 | workgroup 주소 공간의 메모리 및 원자 연산에 영향을 미치는 제어 배리어 동기화 함수를 실행합니다. |
17.11.4.
workgroupUniformLoad
오버로드 |
|
파라미터화 | T 는 구체적 구성 가능 타입입니다.
|
설명 |
워크그룹 내 모든 호출에 p 가 가리키는 값을 반환합니다.
반환값은 균일 값입니다.
p 는 균일
값이어야
합니다.
|
오버로드 |
|
설명 |
p 가 가리키는 값을 원자적으로 로드하여 워크그룹 내 모든 호출에 반환합니다.
반환값은 균일 값입니다.
p 는 균일
값이어야
합니다.
|
17.12. 서브그룹 내장 함수
§ 15.6.3 서브그룹 연산을 참고하세요.
이 함수들에 대한 호출은 다음과 같습니다:
-
트리거는 subgroup_uniformity 경고를 발생시키며, 균일성 분석이 호출이 균일 제어 흐름에 속하는지 증명할 수 없는 경우에 해당합니다.
참고: 컴퓨트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 워크그룹입니다. 프래그먼트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 드로우 명령입니다. 이 두 범위 모두 서브그룹보다 큽니다.
17.12.1.
subgroupAdd
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다.
|
설명 |
축소 연산. |
17.12.1.1. subgroupExclusiveAdd
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다.
|
설명 |
배타적 프리픽스 스캔 연산. 현재 호출의 서브그룹 호출 ID보다 작은
서브그룹 내 모든 활성 호출들
중 활성 호출들 중 가장 낮은 id를 가진 호출의 반환값은 |
17.12.1.2. subgroupInclusiveAdd
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다.
|
설명 |
포괄적 프리픽스 스캔 연산. 현재 호출의 서브그룹 호출 ID보다 작거나 같은
서브그룹 내 모든 활성 호출들
중 참고:
|
17.12.2.
subgroupAll
오버로드 |
|
설명 | 활성된 subgroup 내 모든 호출에서
e 가 true 일 때 true 를 반환합니다.
|
17.12.3.
subgroupAnd
오버로드 |
|
사전조건 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다
|
설명 |
축소 연산. |
17.12.4.
subgroupAny
오버로드 |
|
설명 | 활성된 subgroup 내 어떤 호출이라도
e 가 true 이면 true 를 반환합니다.
|
17.12.5.
subgroupBallot
오버로드 |
|
설명 |
활성된 subgroup 내에서
pred 가 true 인 호출들에 대한 비트마스크를 반환합니다.반환값의 x 컴포넌트는 호출 0부터 31까지를 포함합니다. 각 컴포넌트 내에서는 ID가 비트 위치에 따라 오름차순입니다 (예: ID 32는 y 컴포넌트의 비트 위치 0에 있습니다). |
17.12.6.
subgroupBroadcast
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터I 는 u32 또는 i32입니다
|
설명 |
subgroup 호출 ID가 id 와 일치하는 호출의
e 값을 모든 활성된 subgroup 호출에 반환합니다.
참고: 상수가 아닌 버전의 |
17.12.6.1. subgroupBroadcastFirst
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 | subgroup 호출 ID가 활성 호출 중 가장 낮은 호출의
e 값을 subgroup 내 모든 활성 호출에 반환합니다.
|
17.12.7.
subgroupElect
오버로드 |
|
설명 | 현재 호출이 subgroup 호출 ID가 활성 호출 중 가장 낮을 경우
true 를 반환합니다.
|
17.12.8.
subgroupMax
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
축소 연산. |
17.12.9.
subgroupMin
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
축소 연산. |
17.12.10.
subgroupMul
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
축소 연산. |
17.12.10.1. subgroupExclusiveMul
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
배타적 프리픽스 스캔 연산. 현재 호출의 subgroup 호출 ID보다 작은 subgroup 내 모든 활성 호출의 활성 호출 중 가장 낮은 id의 호출에 대해 반환값은 |
17.12.10.2. subgroupInclusiveMul
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
포괄적 프리픽스 스캔 연산. 현재 호출의 subgroup 호출 ID보다 작거나 같은 subgroup 내 모든 활성 호출의
참고:
|
17.12.11.
subgroupOr
오버로드 |
|
사전조건 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다
|
설명 |
축소 연산. |
17.12.12.
subgroupShuffle
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다I 는 u32 또는 i32입니다
|
설명 |
subgroup 호출 ID가 id 와 일치하는 호출의
e 값을 반환합니다.
만약
|
17.12.12.1. subgroupShuffleDown
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
현재 호출에 대해 subgroup 호출 ID가
subgroup_invocation_id + delta 와 일치하는 호출의 e 값을 반환합니다.
|
17.12.12.2. subgroupShuffleUp
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
현재 호출에 대해 subgroup 호출 ID가
subgroup_invocation_id - delta 와 일치하는 호출의 e 값을 반환합니다.
|
17.12.12.3. subgroupShuffleXor
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
현재 호출에 대해 subgroup 호출 ID가
subgroup_invocation_id ^ mask 와 일치하는 호출의 e 값을 반환합니다.
|
17.12.13.
subgroupXor
오버로드 |
|
사전조건 | T 는 i32, u32, vecN<i32>, 또는 vecN<u32>입니다
|
설명 |
축소 연산. |
17.13. 쿼드 연산
§ 15.6.4 쿼드 연산 참고.
이 함수들에 대한 호출은 다음과 같습니다:
-
트리거는 subgroup_uniformity 경고를 발생시키며, 균일성 분석이 호출이 균일 제어 흐름에 속하는지 증명할 수 없는 경우에 해당합니다.
참고: 컴퓨트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 워크그룹입니다. 프래그먼트 셰이더 스테이지의 경우, 균일 제어 흐름의 범위는 드로우 명령입니다. 이 두 범위 모두 쿼드보다 큽니다.
17.13.1.
quadBroadcast
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다I 는 u32 또는 i32입니다
|
설명 |
쿼드 내에서 quad
호출 ID가 id 와 일치하는 호출의 e 값을 모든 활성된 쿼드 호출에 반환합니다.
참고: subgroupBroadcast와 달리, 현재 상수가 아닌 대안은 없습니다. |
17.13.2.
quadSwapDiagonal
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
쿼드 내에서 반대 좌표를 가진 호출의 e
값을 반환합니다.
즉,
|
17.13.3.
quadSwapX
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
쿼드 내에서 동일한 X 차원을 공유하는 호출의
e 값을 반환합니다.
즉,
|
17.13.4.
quadSwapY
오버로드 |
|
사전조건 | T 는 구체적 숫자 스칼라 또는 숫자 벡터입니다
|
설명 |
쿼드 내에서 동일한 Y 차원을 공유하는 호출의
e 값을 반환합니다.
즉,
|
18. 재귀적 하강 파싱을 위한 문법
이 절은 규범적이지 않습니다.
WGSL 문법은 LALR(1) 파서에 적합한 형태로 명세되어 있습니다. 구현에서는 재귀적 하강 파서를 사용하고 싶을 수도 있습니다.
규범적인 문법은 재귀적 하강 파서에서 직접 사용할 수 없습니다. 여러 규칙이 좌측 재귀적이기 때문입니다. 문법 규칙이 직접 좌측 재귀적인 경우는 정의되는 비단말 기호가 그 생성식의 첫 번째로 등장할 때입니다.
아래는 WGSL 문법을 기계적으로 변환한 것입니다:
-
직접 및 간접 좌측 재귀 제거.
-
공백 생성식(즉, 엡실론 규칙) 피하기.
-
형제 생성식 사이의 공통 접두사를 모으기.
하지만 LL(1)은 아닙니다.
일부 비단말 기호의 여러 생성식은 공통 전방탐색 집합을 가집니다.
예를 들어, attribute
비단말의 모든 생성식은 attr
토큰으로 시작합니다.
더 미묘한 예로는 global_decl
이 있는데, 세 개의 생성식이 attribute *
로 시작하지만
이후 fn
, override
, var
토큰에 의해 구분됩니다.
간결함을 위해 많은 토큰 정의는 반복하지 않습니다. 토큰 정의는 명세의 본문에서 참고하세요.
| '+'
| '-'
| '('
( expression ( ','
expression )* ','
? )?
')'
| compound_assignment_operator
| '='
| '@'
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 ','
?
')'
| '&'
unary_expression ( '&'
unary_expression )*
| '^'
unary_expression ( '^'
unary_expression )*
| '|'
unary_expression ( '|'
unary_expression )*
| 'false'
| 'true'
| 'default'
| '.'
member_ident component_or_swizzle_specifier
?
| '.'
swizzle_name component_or_swizzle_specifier
?
| '['
expression ']'
component_or_swizzle_specifier
?
| '%='
| '&='
| '*='
| '+='
| '-='
| '/='
| '^='
| '|='
| '@'
'compute'
| '@'
'const'
| ident
| '('
lhs_expression ')'
| /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]/
| /0[iu]?/
| /[1-9][0-9]*[iu]?/
| '('
ident_pattern_token ','
diagnostic_rule_name ','
? ')'
| 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
)*
| '@'
'fragment'
| template_elaborated_ident.post.ident
'('
( expression ( ','
expression )* ','
? )?
')'
| 'const_assert'
';'
| 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 )* ','
?
'}'
| 'diagnostic'
'('
ident_pattern_token ','
diagnostic_rule_name
','
? ')'
';'
| 'enable'
ident_pattern_token ( ','
ident_pattern_token )* ','
?
';'
| 'requires'
ident_pattern_token ( ','
ident_pattern_token )* ','
?
';'
| attribute * 'override'
optionally_typed_ident (
'='
expression )?
| 'const'
optionally_typed_ident
'='
expression
| /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]?)?/
| '@'
'interpolate'
'('
ident_pattern_token
','
? ')'
| '@'
'interpolate'
'('
ident_pattern_token
','
ident_pattern_token ','
?
')'
| '@'
'invariant'
| core_lhs_expression component_or_swizzle_specifier ?
| '&'
lhs_expression
| '*'
lhs_expression
| '%'
| '*'
| '/'
| '@'
'must_use'
| ident ( ':'
type_specifier )?
| attribute * ident
':'
type_specifier
| ident template_elaborated_ident.post.ident
| ident template_elaborated_ident.post.ident argument_expression_list
| literal
| '('
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
| ( multiplicative_operator unary_expression )* ( additive_operator unary_expression ( multiplicative_operator unary_expression )* )*
| 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
| 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 ? ';'
| 'case'
case_selector ( ','
case_selector )* ','
?
':'
? compound_statement
| 'default'
':'
? compound_statement
| /[rgba]/
| /[rgba][rgba]/
| /[rgba][rgba][rgba]/
| /[rgba][rgba][rgba][rgba]/
| /[xyzw]/
| /[xyzw][xyzw]/
| /[xyzw][xyzw][xyzw]/
| /[xyzw][xyzw][xyzw][xyzw]/
| ( _template_args_start template_arg_expression (
','
expression )* ','
? _template_args_end )?
| global_directive * ( global_decl | global_assert | ';'
) *
| ident ( _template_args_start template_arg_expression (
','
expression )* ','
? _template_args_end )?
| primary_expression component_or_swizzle_specifier ?
| '!'
unary_expression
| '&'
unary_expression
| '*'
unary_expression
| '-'
unary_expression
| '~'
unary_expression
| 'var'
( _template_args_start expression ( ','
expression )* ','
? _template_args_end )? optionally_typed_ident
| variable_decl '='
expression
| 'const'
optionally_typed_ident
'='
expression
| 'let'
optionally_typed_ident
'='
expression
| lhs_expression ( '='
| compound_assignment_operator
) expression
| lhs_expression '++'
| lhs_expression '--'
| '_'
'='
expression
| '@'
'vertex'
| '@'
'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