///|
fn WgslIrEmitter::emit_block_statements(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
function_plan : WgslIrFunctionWriterPlan,
block : Block,
indent : Int,
) -> Unit raise WgslIrEmitError {
let plan = function_plan.statement_plan(function, block)
let mut index = 0
while index < plan.length() {
let item = plan[index]
if item.skip {
index = index + 1
continue
}
if item.blank_before {
out.write_string("\n")
}
let consumed_next = self.emit_statement_with_following_context(
out,
function,
function_plan,
item.statement,
plan.get(index + 1),
indent,
)
index = if consumed_next { index + 2 } else { index + 1 }
}
}
///|
fn WgslIrEmitter::emit_statement_with_following_context(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
function_plan : WgslIrFunctionWriterPlan,
statement : Statement,
following : WgslIrStatementWriterPlanItem?,
indent : Int,
) -> Bool raise WgslIrEmitError {
match (statement, following) {
(
Switch(selector, cases),
Some({ statement: ImplicitReturn, skip: false, .. }),
) =>
if wgsl_ir_switch_can_absorb_implicit_return(cases) {
self.emit_switch_statement(
out, function, function_plan, selector, cases, true, indent,
)
true
} else {
self.emit_statement(out, function, function_plan, statement, indent)
false
}
(
If(condition, accept, reject),
Some({ statement: ImplicitReturn, skip: false, .. }),
) => {
self.emit_if_statement(
out, function, function_plan, condition, accept, reject, true, indent,
)
true
}
_ => {
self.emit_statement(out, function, function_plan, statement, indent)
false
}
}
}
///|
fn WgslIrEmitter::emit_statement(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
function_plan : WgslIrFunctionWriterPlan,
statement : Statement,
indent : Int,
) -> Unit raise WgslIrEmitError {
let pad = wgsl_ir_indent(indent)
match statement {
Declare(handle) =>
self.emit_local_declaration(out, function, handle, indent)
Emit(range) =>
for index in range.start.index()..<=range.end.index() {
let handle = Handle::Handle(index)
match self.function_named_expression_name(function, handle) {
Some(name) => {
if self.emitted_function_named_expressions.contains(handle.index()) {
continue
}
self.emitted_function_named_expressions.add(handle.index())
let ty = match self.expression_type(Some(function), handle) {
Some(value) => value
None =>
raise MissingHandle("named expression type \{handle.index()}")
}
let value = if self.type_is_pointer(ty) {
match function.expressions.items.get(handle.index()) {
Some(AddressOf(_, _)) =>
self.function_expression_inline(function, handle)
Some(_) =>
self.options.address_of_text(
self.function_pointer_expression(function, handle),
)
None =>
raise MissingHandle(
"named pointer expression \{handle.index()}",
)
}
} else {
self.function_expression_inline(function, handle)
}
out.write_string(
"\{pad}let \{name}\{self.inferred_result_type_annotation(ty)} = \{value};\n",
)
continue
}
None => ()
}
if function_plan.contains_temporary_initializer_expression(handle) {
continue
}
if function_plan.contains_materialized_expression(handle) {
if self.emitted_function_materialized_expressions.contains(
handle.index(),
) {
continue
}
self.emitted_function_materialized_expressions.add(handle.index())
let ty = match self.expression_type(Some(function), handle) {
Some(value) => value
None =>
raise MissingHandle(
"materialized expression type \{handle.index()}",
)
}
out.write_string(
"\{pad}let \{self.function_expression_temporary_name(handle)}\{self.inferred_result_type_annotation(ty)} = \{self.function_expression_inline(function, handle)};\n",
)
continue
}
let expr = self.function_expression(function, handle)
if self.expression_type(Some(function), handle) == None &&
self.function_expression_emits_standalone_statement(function, handle) {
out.write_string("\{pad}\{expr};\n")
} else if self.function_expression_requires_phony_assignment(
function, handle,
) {
out.write_string("\{pad}_ = \{expr};\n")
}
}
Phony(handle) =>
out.write_string(
"\{pad}_ = \{self.function_expression(function, handle)};\n",
)
Block(child) => {
out.write_string("\{pad}{\n")
self.emit_block_statements(
out,
function,
function_plan,
child,
indent + 1,
)
out.write_string("\{pad}}\n")
}
If(condition, accept, reject) =>
self.emit_if_statement(
out, function, function_plan, condition, accept, reject, false, indent,
)
Loop(body, continuing, break_if) => {
out.write_string("\{pad}loop {\n")
self.emit_block_statements(out, function, function_plan, body, indent + 1)
let has_break_if = match break_if {
Some(_) => true
None => false
}
if !continuing.statements.is_empty() || has_break_if {
out.write_string("\{wgsl_ir_indent(indent + 1)}continuing {\n")
self.emit_block_statements(
out,
function,
function_plan,
continuing,
indent + 2,
)
match break_if {
Some(condition) =>
out.write_string(
"\{wgsl_ir_indent(indent + 2)}break if \{self.function_expression(function, condition)};\n",
)
None => ()
}
out.write_string("\{wgsl_ir_indent(indent + 1)}}\n")
}
out.write_string("\{pad}}\n")
}
Switch(selector, cases) =>
self.emit_switch_statement(
out, function, function_plan, selector, cases, false, indent,
)
Break => out.write_string("\{pad}break;\n")
Continue => out.write_string("\{pad}continue;\n")
Return(value) =>
match value {
Some(handle) =>
out.write_string(
"\{pad}return \{self.function_expression(function, handle)};\n",
)
None => out.write_string("\{pad}return;\n")
}
ImplicitReturn => out.write_string("\{pad}return;\n")
Kill => out.write_string("\{pad}discard;\n")
ConstAssert(condition) =>
out.write_string(
"\{pad}const_assert \{self.abstract_float_function_expression(function, condition)};\n",
)
ControlBarrier(barrier) =>
self.emit_control_barrier_statement(out, barrier, indent)
MemoryBarrier(barrier) =>
self.emit_memory_barrier_statement(out, barrier, indent)
Store(pointer, value) =>
out.write_string(
"\{pad}\{self.function_store_pointer_expression(function, pointer)} = \{self.function_store_value_expression(function, pointer, value)};\n",
)
Atomic(pointer, atomic_function, value, compare, result) => {
match result {
Some(result_handle) => {
let ty = match self.expression_type(Some(function), result_handle) {
Some(value) => value
None =>
raise MissingHandle("atomic result type \{result_handle.index()}")
}
out.write_string(
"\{pad}let \{self.function_expression_temporary_name(result_handle)}\{self.inferred_result_type_annotation(ty)} = ",
)
}
None => out.write_string(pad)
}
out.write_string(
"\{self.atomic_function_name(atomic_function)}(\{self.pointer_builtin_argument_expression(Some(function), pointer)}",
)
match compare {
Some(handle) =>
out.write_string(
", \{self.required_function_expression(Some(function), handle)}, \{self.required_function_expression(Some(function), value)}",
)
None =>
out.write_string(
", \{self.required_function_expression(Some(function), value)}",
)
}
out.write_string(");\n")
}
ImageStore(image, coordinate, array_index, value) => {
out.write_string(
"\{pad}textureStore(\{self.function_expression(function, image)}, \{self.function_expression(function, coordinate)}",
)
match array_index {
Some(index) =>
out.write_string(", \{self.function_expression(function, index)}")
None => ()
}
out.write_string(", \{self.function_expression(function, value)});\n")
}
ImageAtomic(image, coordinate, array_index, atomic_function, value) => {
out.write_string(
"\{pad}\{self.texture_atomic_function_name(atomic_function)}(\{self.function_expression(function, image)}, \{self.function_expression(function, coordinate)}",
)
match array_index {
Some(index) =>
out.write_string(", \{self.function_expression(function, index)}")
None => ()
}
out.write_string(", \{self.function_expression(function, value)});\n")
}
Call(function_handle, arguments, result) => {
match result {
Some(result_handle) => {
let ty = match self.expression_type(Some(function), result_handle) {
Some(value) => value
None =>
raise MissingHandle("call result type \{result_handle.index()}")
}
out.write_string(
"\{pad}let \{self.function_expression_temporary_name(result_handle)}\{self.inferred_result_type_annotation(ty)} = ",
)
}
None => out.write_string(pad)
}
out.write_string("\{self.function_name(function_handle)}(")
self.emit_function_call_arguments(
out, function, function_handle, arguments,
)
out.write_string(");\n")
}
RayQuery(query, query_function) =>
self.emit_ray_query_statement(
out, function, query, query_function, indent,
)
CooperativeStore(target, data) =>
self.emit_cooperative_store_statement(out, function, target, data, indent)
_ => raise Unsupported("statement kind")
}
}
///|
fn WgslIrEmitter::emit_if_statement(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
function_plan : WgslIrFunctionWriterPlan,
condition : Handle,
accept : Block,
reject : Block,
absorb_implicit_return : Bool,
indent : Int,
) -> Unit raise WgslIrEmitError {
let pad = wgsl_ir_indent(indent)
out.write_string(
"\{pad}if \{self.function_expression(function, condition)} {\n",
)
self.emit_block_statements(out, function, function_plan, accept, indent + 1)
if absorb_implicit_return && !wgsl_ir_block_ends_with_return(accept) {
out.write_string("\{wgsl_ir_indent(indent + 1)}return;\n")
}
out.write_string("\{pad}}")
if reject.statements.is_empty() && !absorb_implicit_return {
out.write_string("\n")
} else {
out.write_string(" else {\n")
self.emit_block_statements(out, function, function_plan, reject, indent + 1)
if absorb_implicit_return && !wgsl_ir_block_ends_with_return(reject) {
out.write_string("\{wgsl_ir_indent(indent + 1)}return;\n")
}
out.write_string("\{pad}}\n")
}
}
///|
fn WgslIrEmitter::function_store_value_expression(
self : WgslIrEmitter,
function : Function,
pointer : Handle,
value : Handle,
) -> String raise WgslIrEmitError {
let scalar_context = if self.options.contextualize_numeric_literals() {
match self.expression_type(Some(function), pointer) {
Some(pointer_ty) => {
let value_ty = self.deref_pointer_type_handle(pointer_ty)
self.store_numeric_literal_context(value_ty)
}
None => None
}
} else {
None
}
self.contextual_argument_expression(Some(function), value, scalar_context)
}
///|
fn WgslIrEmitter::store_numeric_literal_context(
self : WgslIrEmitter,
value_ty : Handle,
) -> Scalar? {
match self.shader_module.types.items.get(value_ty.index()) {
Some(type_) =>
match type_.inner {
Scalar(scalar) | Vector(_, scalar) => Some(scalar)
_ => None
}
None => None
}
}
///|
fn WgslIrEmitter::function_expression_emits_standalone_statement(
self : WgslIrEmitter,
function : Function,
handle : Handle,
) -> Bool {
ignore(self)
match function.expressions.items.get(handle.index()) {
Some(RayQueryInitialize(_, _, _))
| Some(RayQueryProceed(_))
| Some(RayQueryGenerateIntersection(_, _))
| Some(RayQueryConfirmIntersection(_))
| Some(RayQueryTerminate(_)) => true
_ => false
}
}
///|
fn WgslIrEmitter::function_expression_requires_phony_assignment(
self : WgslIrEmitter,
function : Function,
handle : Handle,
) -> Bool {
ignore(self)
match function.expressions.items.get(handle.index()) {
Some(expression) =>
match expression {
ImageSample(_, _, _, _, _, _, _, _, _)
| ImageLoad(_, _, _, _, _)
| ImageQuery(_, _)
| WorkGroupUniformLoad(_)
| SubgroupCall(_, _) => true
_ => false
}
None => false
}
}
///|
fn WgslIrEmitter::emit_switch_statement(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
function_plan : WgslIrFunctionWriterPlan,
selector : Handle,
cases : Array[SwitchCase],
absorb_implicit_return : Bool,
indent : Int,
) -> Unit raise WgslIrEmitError {
let pad = wgsl_ir_indent(indent)
out.write_string(
"\{pad}switch \{self.function_expression(function, selector)} {\n",
)
for case in cases {
out.write_string(
"\{wgsl_ir_indent(indent + 1)}\{wgsl_ir_switch_case_label(case.value)}: {\n",
)
self.emit_block_statements(
out,
function,
function_plan,
case.body,
indent + 2,
)
if absorb_implicit_return && !wgsl_ir_block_ends_with_return(case.body) {
out.write_string("\{wgsl_ir_indent(indent + 2)}return;\n")
}
out.write_string("\{wgsl_ir_indent(indent + 1)}}\n")
}
out.write_string("\{pad}}\n")
}
///|
fn wgsl_ir_switch_can_absorb_implicit_return(cases : Array[SwitchCase]) -> Bool {
let mut has_default = false
for case in cases {
if case.fall_through {
return false
}
match case.value {
Default => has_default = true
_ => ()
}
}
has_default
}
///|
fn WgslIrEmitter::function_store_pointer_expression(
self : WgslIrEmitter,
function : Function,
handle : Handle,
) -> String raise WgslIrEmitError {
let expression = match function.expressions.items.get(handle.index()) {
Some(value) => value
None =>
raise MissingHandle("function store pointer expression \{handle.index()}")
}
let text = self.function_pointer_expression(function, handle)
match expression {
Access(_, _) | AccessIndex(_, _) | Component(_, _) | Swizzle(_, _, _) =>
text
_ =>
match self.expression_type(Some(function), handle) {
Some(ty) =>
match self.shader_module.types.items.get(ty.index()) {
Some(type_) =>
match type_.inner {
Pointer(_, _) => self.options.pointer_dereference_text(text)
_ => text
}
None => text
}
None => text
}
}
}
///|
fn WgslIrEmitter::emit_control_barrier_statement(
self : WgslIrEmitter,
out : StringBuilder,
barrier : Barrier,
indent : Int,
) -> Unit raise WgslIrEmitError {
let pad = wgsl_ir_indent(indent)
if barrier == Barrier::work_group() {
out.write_string("\{pad}workgroupBarrier();\n")
} else if barrier == Barrier::sub_group() {
out.write_string("\{pad}subgroupBarrier();\n")
} else {
ignore(self)
raise Unsupported("control barrier flags")
}
}
///|
fn WgslIrEmitter::emit_memory_barrier_statement(
self : WgslIrEmitter,
out : StringBuilder,
barrier : Barrier,
indent : Int,
) -> Unit raise WgslIrEmitError {
let pad = wgsl_ir_indent(indent)
if barrier == Barrier::storage() {
out.write_string("\{pad}storageBarrier();\n")
} else if barrier == Barrier::texture() {
out.write_string("\{pad}textureBarrier();\n")
} else {
ignore(self)
raise Unsupported("memory barrier flags")
}
}
///|
fn WgslIrEmitter::emit_ray_query_statement(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
query : Handle,
query_function : RayQueryFunction,
indent : Int,
) -> Unit raise WgslIrEmitError {
let pad = wgsl_ir_indent(indent)
match query_function {
Initialize(acceleration, ray_desc) =>
out.write_string(
"\{pad}rayQueryInitialize(\{self.function_expression(function, query)}, \{self.function_expression(function, acceleration)}, \{self.function_expression(function, ray_desc)});\n",
)
Proceed(_) =>
out.write_string(
"\{pad}rayQueryProceed(\{self.function_expression(function, query)});\n",
)
GenerateIntersection(intersection_t) =>
out.write_string(
"\{pad}rayQueryGenerateIntersection(\{self.function_expression(function, query)}, \{self.function_expression(function, intersection_t)});\n",
)
ConfirmIntersection =>
out.write_string(
"\{pad}rayQueryConfirmIntersection(\{self.function_expression(function, query)});\n",
)
Terminate =>
out.write_string(
"\{pad}rayQueryTerminate(\{self.function_expression(function, query)});\n",
)
}
}
///|
fn wgsl_ir_switch_case_label(value : SwitchValue) -> String {
match value {
I32(value, SourceAbstractInt) => "case \{value}"
I32(value, Typed) => "case \{value}i"
U32(value, SourceAbstractInt) => "case \{value}"
U32(value, Typed) => "case \{value}u"
Default => "default"
}
}
///|
fn WgslIrEmitter::emit_function_call_arguments(
self : WgslIrEmitter,
out : StringBuilder,
function : Function,
callee : Handle,
arguments : Array[Handle],
) -> Unit raise WgslIrEmitError {
let callee_function = self.shader_module.functions.items.get(callee.index())
for index in 0.. 0 {
out.write_string(", ")
}
let scalar_context = if self.options.contextualize_numeric_literals() {
match callee_function {
Some(callee_function) =>
match callee_function.arguments.get(index) {
Some(argument) => self.type_component_scalar(argument.ty)
None => None
}
None => None
}
} else {
None
}
out.write_string(
self.function_call_argument_expression(
Some(function),
callee_function,
index,
arguments[index],
scalar_context,
),
)
}
}