|
|
@ -3,6 +3,7 @@ |
|
|
// Refer to the license.txt file included.
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
|
#include <tuple>
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
|
|
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
|
|
|
|
|
|
|
|
@ -29,6 +30,15 @@ std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { |
|
|
throw InvalidArgument("Invalid attribute type {}", type); |
|
|
throw InvalidArgument("Invalid attribute type {}", type); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename... Args> |
|
|
|
|
|
Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) { |
|
|
|
|
|
if (ctx.stage == Stage::Geometry) { |
|
|
|
|
|
return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...); |
|
|
|
|
|
} else { |
|
|
|
|
|
return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { |
|
|
std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { |
|
|
const u32 element{static_cast<u32>(attr) % 4}; |
|
|
const u32 element{static_cast<u32>(attr) % 4}; |
|
|
const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; |
|
|
const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; |
|
|
@ -66,6 +76,31 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { |
|
|
throw NotImplementedException("Read attribute {}", attr); |
|
|
throw NotImplementedException("Read attribute {}", attr); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, |
|
|
|
|
|
const IR::Value& binding, const IR::Value& offset) { |
|
|
|
|
|
if (!binding.IsImmediate()) { |
|
|
|
|
|
throw NotImplementedException("Constant buffer indexing"); |
|
|
|
|
|
} |
|
|
|
|
|
const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; |
|
|
|
|
|
const Id uniform_type{ctx.uniform_types.*member_ptr}; |
|
|
|
|
|
if (!offset.IsImmediate()) { |
|
|
|
|
|
Id index{ctx.Def(offset)}; |
|
|
|
|
|
if (element_size > 1) { |
|
|
|
|
|
const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; |
|
|
|
|
|
const Id shift{ctx.Constant(ctx.U32[1], log2_element_size)}; |
|
|
|
|
|
index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); |
|
|
|
|
|
} |
|
|
|
|
|
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; |
|
|
|
|
|
return ctx.OpLoad(result_type, access_chain); |
|
|
|
|
|
} |
|
|
|
|
|
if (offset.U32() % element_size != 0) { |
|
|
|
|
|
throw NotImplementedException("Unaligned immediate constant buffer load"); |
|
|
|
|
|
} |
|
|
|
|
|
const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / element_size)}; |
|
|
|
|
|
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; |
|
|
|
|
|
return ctx.OpLoad(result_type, access_chain); |
|
|
|
|
|
} |
|
|
} // Anonymous namespace
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
|
void EmitGetRegister(EmitContext&) { |
|
|
void EmitGetRegister(EmitContext&) { |
|
|
@ -100,31 +135,6 @@ void EmitGetIndirectBranchVariable(EmitContext&) { |
|
|
throw NotImplementedException("SPIR-V Instruction"); |
|
|
throw NotImplementedException("SPIR-V Instruction"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, |
|
|
|
|
|
u32 element_size, const IR::Value& binding, const IR::Value& offset) { |
|
|
|
|
|
if (!binding.IsImmediate()) { |
|
|
|
|
|
throw NotImplementedException("Constant buffer indexing"); |
|
|
|
|
|
} |
|
|
|
|
|
const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; |
|
|
|
|
|
const Id uniform_type{ctx.uniform_types.*member_ptr}; |
|
|
|
|
|
if (!offset.IsImmediate()) { |
|
|
|
|
|
Id index{ctx.Def(offset)}; |
|
|
|
|
|
if (element_size > 1) { |
|
|
|
|
|
const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; |
|
|
|
|
|
const Id shift{ctx.Constant(ctx.U32[1], log2_element_size)}; |
|
|
|
|
|
index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); |
|
|
|
|
|
} |
|
|
|
|
|
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; |
|
|
|
|
|
return ctx.OpLoad(result_type, access_chain); |
|
|
|
|
|
} |
|
|
|
|
|
if (offset.U32() % element_size != 0) { |
|
|
|
|
|
throw NotImplementedException("Unaligned immediate constant buffer load"); |
|
|
|
|
|
} |
|
|
|
|
|
const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / element_size)}; |
|
|
|
|
|
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; |
|
|
|
|
|
return ctx.OpLoad(result_type, access_chain); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
|
|
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
|
|
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; |
|
|
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; |
|
|
return ctx.OpUConvert(ctx.U32[1], load); |
|
|
return ctx.OpUConvert(ctx.U32[1], load); |
|
|
@ -157,7 +167,7 @@ Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& |
|
|
return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset); |
|
|
return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { |
|
|
|
|
|
|
|
|
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { |
|
|
const u32 element{static_cast<u32>(attr) % 4}; |
|
|
const u32 element{static_cast<u32>(attr) % 4}; |
|
|
const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; |
|
|
const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; |
|
|
if (IR::IsGeneric(attr)) { |
|
|
if (IR::IsGeneric(attr)) { |
|
|
@ -168,7 +178,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { |
|
|
return ctx.Constant(ctx.F32[1], 0.0f); |
|
|
return ctx.Constant(ctx.F32[1], 0.0f); |
|
|
} |
|
|
} |
|
|
const Id generic_id{ctx.input_generics.at(index)}; |
|
|
const Id generic_id{ctx.input_generics.at(index)}; |
|
|
const Id pointer{ctx.OpAccessChain(type->pointer, generic_id, element_id())}; |
|
|
|
|
|
|
|
|
const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, element_id())}; |
|
|
const Id value{ctx.OpLoad(type->id, pointer)}; |
|
|
const Id value{ctx.OpLoad(type->id, pointer)}; |
|
|
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; |
|
|
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; |
|
|
} |
|
|
} |
|
|
@ -177,8 +187,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { |
|
|
case IR::Attribute::PositionY: |
|
|
case IR::Attribute::PositionY: |
|
|
case IR::Attribute::PositionZ: |
|
|
case IR::Attribute::PositionZ: |
|
|
case IR::Attribute::PositionW: |
|
|
case IR::Attribute::PositionW: |
|
|
return ctx.OpLoad(ctx.F32[1], |
|
|
|
|
|
ctx.OpAccessChain(ctx.input_f32, ctx.input_position, element_id())); |
|
|
|
|
|
|
|
|
return ctx.OpLoad( |
|
|
|
|
|
ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, element_id())); |
|
|
case IR::Attribute::InstanceId: |
|
|
case IR::Attribute::InstanceId: |
|
|
if (ctx.profile.support_vertex_instance_id) { |
|
|
if (ctx.profile.support_vertex_instance_id) { |
|
|
return ctx.OpLoad(ctx.U32[1], ctx.instance_id); |
|
|
return ctx.OpLoad(ctx.U32[1], ctx.instance_id); |
|
|
@ -198,29 +208,32 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { |
|
|
ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()), |
|
|
ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()), |
|
|
ctx.u32_zero_value); |
|
|
ctx.u32_zero_value); |
|
|
case IR::Attribute::PointSpriteS: |
|
|
case IR::Attribute::PointSpriteS: |
|
|
return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, |
|
|
|
|
|
ctx.Constant(ctx.U32[1], 0U))); |
|
|
|
|
|
|
|
|
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, |
|
|
|
|
|
ctx.u32_zero_value)); |
|
|
case IR::Attribute::PointSpriteT: |
|
|
case IR::Attribute::PointSpriteT: |
|
|
return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, |
|
|
|
|
|
ctx.Constant(ctx.U32[1], 1U))); |
|
|
|
|
|
|
|
|
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, |
|
|
|
|
|
ctx.Constant(ctx.U32[1], 1U))); |
|
|
default: |
|
|
default: |
|
|
throw NotImplementedException("Read attribute {}", attr); |
|
|
throw NotImplementedException("Read attribute {}", attr); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value) { |
|
|
|
|
|
|
|
|
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) { |
|
|
const std::optional<Id> output{OutputAttrPointer(ctx, attr)}; |
|
|
const std::optional<Id> output{OutputAttrPointer(ctx, attr)}; |
|
|
if (!output) { |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
if (output) { |
|
|
|
|
|
ctx.OpStore(*output, value); |
|
|
} |
|
|
} |
|
|
ctx.OpStore(*output, value); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset) { |
|
|
|
|
|
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); |
|
|
|
|
|
|
|
|
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) { |
|
|
|
|
|
if (ctx.stage == Stage::Geometry) { |
|
|
|
|
|
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex); |
|
|
|
|
|
} else { |
|
|
|
|
|
return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value) { |
|
|
|
|
|
|
|
|
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unused]] Id vertex) { |
|
|
ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); |
|
|
ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|