shader: Properly scale image reads and add GL SPIR-V support
Thanks for everything!
This commit is contained in:
parent
fc9bb3c3fe
commit
e66d5b88a6
25 changed files with 228 additions and 77 deletions
|
@ -14,6 +14,8 @@ struct Bindings {
|
|||
u32 storage_buffer{};
|
||||
u32 texture{};
|
||||
u32 image{};
|
||||
u32 texture_scaling_index{};
|
||||
u32 image_scaling_index{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
@ -55,7 +56,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
|||
}
|
||||
if (!runtime_info.glasm_use_storage_buffers) {
|
||||
if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
|
||||
Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1);
|
||||
const size_t index{num + PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE};
|
||||
Add("PARAM c[{}]={{program.local[0..{}]}};", index, index - 1);
|
||||
}
|
||||
}
|
||||
stage = program.stage;
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
constexpr u32 PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE = 1;
|
||||
|
||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
|
|
|
@ -617,6 +617,15 @@ void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
|
|||
1u << index.U32(), ctx.reg_alloc.Define(inst));
|
||||
}
|
||||
|
||||
void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index) {
|
||||
if (!index.IsImmediate()) {
|
||||
throw NotImplementedException("Non-constant texture rescaling");
|
||||
}
|
||||
ctx.Add("AND.U RC.x,scaling[0].y,{};"
|
||||
"SNE.S {},RC.x,0;",
|
||||
1u << index.U32(), ctx.reg_alloc.Define(inst));
|
||||
}
|
||||
|
||||
void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "ADD.U32");
|
||||
|
|
|
@ -557,6 +557,7 @@ void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Reg
|
|||
void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
Register color);
|
||||
void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index);
|
||||
void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index);
|
||||
void EmitBindlessImageAtomicIAdd32(EmitContext&);
|
||||
void EmitBindlessImageAtomicSMin32(EmitContext&);
|
||||
void EmitBindlessImageAtomicUMin32(EmitContext&);
|
||||
|
|
|
@ -211,7 +211,7 @@ void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
|||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.F {}.x,scaling[0].y;", inst);
|
||||
ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
|
|
|
@ -446,7 +446,7 @@ void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
|||
}
|
||||
|
||||
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddF32("{}=scaling.y;", inst);
|
||||
ctx.AddF32("{}=scaling.z;", inst);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
|
||||
|
|
|
@ -620,6 +620,14 @@ void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
|
|||
ctx.AddU1("{}=(ftou(scaling.x)&{})!=0;", inst, 1u << image_index);
|
||||
}
|
||||
|
||||
void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index) {
|
||||
if (!index.IsImmediate()) {
|
||||
throw NotImplementedException("Non-constant texture rescaling");
|
||||
}
|
||||
const u32 image_index{index.U32()};
|
||||
ctx.AddU1("{}=(ftou(scaling.y)&{})!=0;", inst, 1u << image_index);
|
||||
}
|
||||
|
||||
void EmitBindlessImageSampleImplicitLod(EmitContext&) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_context.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
|
@ -476,8 +477,9 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
|
|||
|
||||
EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
|
||||
IR::Program& program, Bindings& bindings)
|
||||
: Sirit::Module(profile_.supported_spirv), profile{profile_},
|
||||
runtime_info{runtime_info_}, stage{program.stage} {
|
||||
: Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
|
||||
stage{program.stage}, texture_rescaling_index{bindings.texture_scaling_index},
|
||||
image_rescaling_index{bindings.image_scaling_index} {
|
||||
const bool is_unified{profile.unified_descriptor_binding};
|
||||
u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
|
||||
u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
|
||||
|
@ -494,8 +496,8 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
|
|||
DefineStorageBuffers(program.info, storage_binding);
|
||||
DefineTextureBuffers(program.info, texture_binding);
|
||||
DefineImageBuffers(program.info, image_binding);
|
||||
DefineTextures(program.info, texture_binding);
|
||||
DefineImages(program.info, image_binding);
|
||||
DefineTextures(program.info, texture_binding, bindings.texture_scaling_index);
|
||||
DefineImages(program.info, image_binding, bindings.image_scaling_index);
|
||||
DefineAttributeMemAccess(program.info);
|
||||
DefineGlobalMemoryFunctions(program.info);
|
||||
DefineRescalingInput(program.info);
|
||||
|
@ -1003,25 +1005,49 @@ void EmitContext::DefineRescalingInput(const Info& info) {
|
|||
if (!info.uses_rescaling_uniform) {
|
||||
return;
|
||||
}
|
||||
boost::container::static_vector<Id, 2> members{F32[1]};
|
||||
if (profile.unified_descriptor_binding) {
|
||||
DefineRescalingInputPushConstant(info);
|
||||
} else {
|
||||
DefineRescalingInputUniformConstant();
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineRescalingInputPushConstant(const Info& info) {
|
||||
boost::container::static_vector<Id, 3> members{F32[1]};
|
||||
u32 member_index{0};
|
||||
const u32 num_texture_words{Common::DivCeil(runtime_info.num_textures, 32u)};
|
||||
if (runtime_info.num_textures > 0) {
|
||||
rescaling_textures_type = TypeArray(U32[1], Const(num_texture_words));
|
||||
if (!info.texture_descriptors.empty()) {
|
||||
rescaling_textures_type = TypeArray(U32[1], Const(4u));
|
||||
Decorate(rescaling_textures_type, spv::Decoration::ArrayStride, 4u);
|
||||
members.push_back(rescaling_textures_type);
|
||||
rescaling_textures_member_index = ++member_index;
|
||||
}
|
||||
if (!info.image_descriptors.empty()) {
|
||||
rescaling_images_type = TypeArray(U32[1], Const(NUM_IMAGE_SCALING_WORDS));
|
||||
if (rescaling_textures_type.value != rescaling_images_type.value) {
|
||||
Decorate(rescaling_images_type, spv::Decoration::ArrayStride, 4u);
|
||||
}
|
||||
members.push_back(rescaling_images_type);
|
||||
rescaling_images_member_index = ++member_index;
|
||||
}
|
||||
const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))};
|
||||
Decorate(push_constant_struct, spv::Decoration::Block);
|
||||
Name(push_constant_struct, "ResolutionInfo");
|
||||
|
||||
MemberDecorate(push_constant_struct, 0u, spv::Decoration::Offset, 0u);
|
||||
MemberName(push_constant_struct, 0u, "down_factor");
|
||||
if (runtime_info.num_textures > 0) {
|
||||
MemberDecorate(push_constant_struct, rescaling_textures_member_index,
|
||||
spv::Decoration::Offset, 4u);
|
||||
|
||||
const u32 offset_bias = stage == Stage::Compute ? sizeof(u32) : 0;
|
||||
if (!info.texture_descriptors.empty()) {
|
||||
MemberDecorate(
|
||||
push_constant_struct, rescaling_textures_member_index, spv::Decoration::Offset,
|
||||
static_cast<u32>(offsetof(RescalingLayout, rescaling_textures) - offset_bias));
|
||||
MemberName(push_constant_struct, rescaling_textures_member_index, "rescaling_textures");
|
||||
}
|
||||
if (!info.image_descriptors.empty()) {
|
||||
MemberDecorate(push_constant_struct, rescaling_images_member_index, spv::Decoration::Offset,
|
||||
static_cast<u32>(offsetof(RescalingLayout, rescaling_images) - offset_bias));
|
||||
MemberName(push_constant_struct, rescaling_images_member_index, "rescaling_images");
|
||||
}
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)};
|
||||
rescaling_push_constants = AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant);
|
||||
Name(rescaling_push_constants, "rescaling_push_constants");
|
||||
|
@ -1031,6 +1057,17 @@ void EmitContext::DefineRescalingInput(const Info& info) {
|
|||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineRescalingInputUniformConstant() {
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, F32[4])};
|
||||
rescaling_uniform_constant =
|
||||
AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant);
|
||||
Decorate(rescaling_uniform_constant, spv::Decoration::Location, 0u);
|
||||
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(rescaling_uniform_constant);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
|
||||
if (info.constant_buffer_descriptors.empty()) {
|
||||
return;
|
||||
|
@ -1219,7 +1256,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
|||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineTextures(const Info& info, u32& binding) {
|
||||
void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) {
|
||||
textures.reserve(info.texture_descriptors.size());
|
||||
for (const TextureDescriptor& desc : info.texture_descriptors) {
|
||||
const Id image_type{ImageType(*this, desc)};
|
||||
|
@ -1241,13 +1278,14 @@ void EmitContext::DefineTextures(const Info& info, u32& binding) {
|
|||
interfaces.push_back(id);
|
||||
}
|
||||
++binding;
|
||||
++scaling_index;
|
||||
}
|
||||
if (info.uses_atomic_image_u32) {
|
||||
image_u32 = TypePointer(spv::StorageClass::Image, U32[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineImages(const Info& info, u32& binding) {
|
||||
void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_index) {
|
||||
images.reserve(info.image_descriptors.size());
|
||||
for (const ImageDescriptor& desc : info.image_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
|
@ -1268,6 +1306,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding) {
|
|||
interfaces.push_back(id);
|
||||
}
|
||||
++binding;
|
||||
++scaling_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -238,9 +238,14 @@ public:
|
|||
Id indexed_load_func{};
|
||||
Id indexed_store_func{};
|
||||
|
||||
Id rescaling_uniform_constant{};
|
||||
Id rescaling_push_constants{};
|
||||
Id rescaling_textures_type{};
|
||||
Id rescaling_images_type{};
|
||||
u32 rescaling_textures_member_index{};
|
||||
u32 rescaling_images_member_index{};
|
||||
u32 texture_rescaling_index{};
|
||||
u32 image_rescaling_index{};
|
||||
|
||||
Id local_memory{};
|
||||
|
||||
|
@ -314,11 +319,13 @@ private:
|
|||
void DefineStorageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextureBuffers(const Info& info, u32& binding);
|
||||
void DefineImageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextures(const Info& info, u32& binding);
|
||||
void DefineImages(const Info& info, u32& binding);
|
||||
void DefineTextures(const Info& info, u32& binding, u32& scaling_index);
|
||||
void DefineImages(const Info& info, u32& binding, u32& scaling_index);
|
||||
void DefineAttributeMemAccess(const Info& info);
|
||||
void DefineGlobalMemoryFunctions(const Info& info);
|
||||
void DefineRescalingInput(const Info& info);
|
||||
void DefineRescalingInputPushConstant(const Info& info);
|
||||
void DefineRescalingInputUniformConstant();
|
||||
|
||||
void DefineInputs(const IR::Program& program);
|
||||
void DefineOutputs(const IR::Program& program);
|
||||
|
|
|
@ -16,15 +16,23 @@
|
|||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
constexpr u32 NUM_TEXTURE_SCALING_WORDS = 4;
|
||||
constexpr u32 NUM_IMAGE_SCALING_WORDS = 2;
|
||||
constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
|
||||
NUM_TEXTURE_SCALING_WORDS + NUM_IMAGE_SCALING_WORDS;
|
||||
|
||||
struct RescalingLayout {
|
||||
u32 down_factor;
|
||||
std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
|
||||
std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
||||
RuntimeInfo runtime_info{};
|
||||
runtime_info.num_textures = Shader::NumDescriptors(program.info.texture_descriptors);
|
||||
|
||||
Bindings binding;
|
||||
return EmitSPIRV(profile, runtime_info, program, binding);
|
||||
return EmitSPIRV(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -527,10 +527,15 @@ Id EmitYDirection(EmitContext& ctx) {
|
|||
}
|
||||
|
||||
Id EmitResolutionDownFactor(EmitContext& ctx) {
|
||||
const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[1])};
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(pointer_type, ctx.rescaling_push_constants, ctx.u32_zero_value)};
|
||||
return ctx.OpLoad(ctx.F32[1], pointer);
|
||||
if (ctx.profile.unified_descriptor_binding) {
|
||||
const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[1])};
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(pointer_type, ctx.rescaling_push_constants, ctx.u32_zero_value)};
|
||||
return ctx.OpLoad(ctx.F32[1], pointer);
|
||||
} else {
|
||||
const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
|
||||
return ctx.OpCompositeExtract(ctx.F32[1], composite, 2u);
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
|
||||
|
|
|
@ -224,6 +224,40 @@ Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx
|
|||
Decorate(ctx, inst, sample);
|
||||
return ctx.OpCompositeExtract(result_type, sample, 1U);
|
||||
}
|
||||
|
||||
Id IsScaled(EmitContext& ctx, const IR::Value& index, Id member_index, u32 base_index) {
|
||||
const Id push_constant_u32{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.U32[1])};
|
||||
Id bit{};
|
||||
if (index.IsImmediate()) {
|
||||
// Use BitwiseAnd instead of BitfieldExtract for better codegen on Nvidia OpenGL.
|
||||
// LOP32I.NZ is used to set the predicate rather than BFE+ISETP.
|
||||
const u32 index_value{index.U32() + base_index};
|
||||
const Id word_index{ctx.Const(index_value / 32)};
|
||||
const Id bit_index_mask{ctx.Const(1u << (index_value % 32))};
|
||||
const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
|
||||
member_index, word_index)};
|
||||
const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
|
||||
bit = ctx.OpBitwiseAnd(ctx.U32[1], word, bit_index_mask);
|
||||
} else {
|
||||
Id index_value{ctx.Def(index)};
|
||||
if (base_index != 0) {
|
||||
index_value = ctx.OpIAdd(ctx.U32[1], index_value, ctx.Const(base_index));
|
||||
}
|
||||
const Id word_index{ctx.OpShiftRightArithmetic(ctx.U32[1], index_value, ctx.Const(5u))};
|
||||
const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
|
||||
member_index, word_index)};
|
||||
const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
|
||||
const Id bit_index{ctx.OpBitwiseAnd(ctx.U32[1], index_value, ctx.Const(31u))};
|
||||
bit = ctx.OpBitFieldUExtract(ctx.U32[1], index_value, bit_index, ctx.Const(1u));
|
||||
}
|
||||
return ctx.OpINotEqual(ctx.U1, bit, ctx.u32_zero_value);
|
||||
}
|
||||
|
||||
Id BitTest(EmitContext& ctx, Id mask, Id bit) {
|
||||
const Id shifted{ctx.OpShiftRightLogical(ctx.U32[1], mask, bit)};
|
||||
const Id bit_value{ctx.OpBitwiseAnd(ctx.U32[1], shifted, ctx.Const(1u))};
|
||||
return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
|
||||
|
@ -471,29 +505,27 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
|
|||
}
|
||||
|
||||
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
|
||||
const Id push_constant_u32{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.U32[1])};
|
||||
const Id member_index{ctx.Const(ctx.rescaling_textures_member_index)};
|
||||
Id bit{};
|
||||
if (index.IsImmediate()) {
|
||||
// Use BitwiseAnd instead of BitfieldExtract for better codegen on Nvidia OpenGL.
|
||||
// LOP32I.NZ is used to set the predicate rather than BFE+ISETP.
|
||||
const u32 index_value{index.U32()};
|
||||
const Id word_index{ctx.Const(index_value / 32)};
|
||||
const Id bit_index_mask{ctx.Const(1u << (index_value % 32))};
|
||||
const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
|
||||
member_index, word_index)};
|
||||
const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
|
||||
bit = ctx.OpBitwiseAnd(ctx.U32[1], word, bit_index_mask);
|
||||
if (ctx.profile.unified_descriptor_binding) {
|
||||
const Id member_index{ctx.Const(ctx.rescaling_textures_member_index)};
|
||||
return IsScaled(ctx, index, member_index, ctx.texture_rescaling_index);
|
||||
} else {
|
||||
const Id index_value{ctx.Def(index)};
|
||||
const Id word_index{ctx.OpShiftRightArithmetic(ctx.U32[1], index_value, ctx.Const(5u))};
|
||||
const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
|
||||
member_index, word_index)};
|
||||
const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
|
||||
const Id bit_index{ctx.OpBitwiseAnd(ctx.U32[1], index_value, ctx.Const(31u))};
|
||||
bit = ctx.OpBitFieldUExtract(ctx.U32[1], index_value, bit_index, ctx.Const(1u));
|
||||
const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
|
||||
const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 0u)};
|
||||
const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
|
||||
return BitTest(ctx, mask, ctx.Def(index));
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitIsImageScaled(EmitContext& ctx, const IR::Value& index) {
|
||||
if (ctx.profile.unified_descriptor_binding) {
|
||||
const Id member_index{ctx.Const(ctx.rescaling_images_member_index)};
|
||||
return IsScaled(ctx, index, member_index, ctx.image_rescaling_index);
|
||||
} else {
|
||||
const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
|
||||
const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 1u)};
|
||||
const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
|
||||
return BitTest(ctx, mask, ctx.Def(index));
|
||||
}
|
||||
return ctx.OpINotEqual(ctx.U1, bit, ctx.u32_zero_value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -514,6 +514,7 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
|
|||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
|
||||
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
|
||||
Id EmitIsImageScaled(EmitContext& ctx, const IR::Value& index);
|
||||
Id EmitBindlessImageAtomicIAdd32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicSMin32(EmitContext&);
|
||||
Id EmitBindlessImageAtomicUMin32(EmitContext&);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue