[vk, gl, spv] Opcode Promotion path emulation

This commit is contained in:
CamilleLaVey 2025-11-29 14:06:18 -04:00 committed by Caio Oliveira
parent f5172f88e8
commit 9dccd2013d
No known key found for this signature in database
GPG key ID: AAAE6C7FD4186B0C
11 changed files with 391 additions and 75 deletions

View file

@ -4,15 +4,162 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/backend/spirv/texture_helpers.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
namespace Shader::Backend::SPIRV {
namespace {
TextureType GetEffectiveType(const EmitContext& ctx, TextureType type) {
return EffectiveTextureType(ctx.profile, type);
}
bool HasLayerComponent(TextureType type) {
switch (type) {
case TextureType::ColorArray1D:
case TextureType::ColorArray2D:
case TextureType::ColorArrayCube:
return true;
default:
return false;
}
}
u32 BaseDimension(TextureType type) {
switch (type) {
case TextureType::Color1D:
case TextureType::ColorArray1D:
return 1;
case TextureType::Color2D:
case TextureType::Color2DRect:
case TextureType::ColorArray2D:
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
return 2;
case TextureType::Color3D:
return 3;
default:
return 0;
}
}
bool IsFloatCoordType(IR::Type type) {
switch (type) {
case IR::Type::F32:
case IR::Type::F32x2:
case IR::Type::F32x3:
case IR::Type::F32x4:
return true;
case IR::Type::U32:
case IR::Type::U32x2:
case IR::Type::U32x3:
case IR::Type::U32x4:
return false;
default:
throw InvalidArgument("Unsupported coordinate type {}", type);
}
}
u32 NumComponents(IR::Type type) {
switch (type) {
case IR::Type::F32:
case IR::Type::U32:
return 1;
case IR::Type::F32x2:
case IR::Type::U32x2:
return 2;
case IR::Type::F32x3:
case IR::Type::U32x3:
return 3;
case IR::Type::F32x4:
case IR::Type::U32x4:
return 4;
default:
throw InvalidArgument("Unsupported type for coordinate promotion {}", type);
}
}
Id ExtractComponent(EmitContext& ctx, Id value, IR::Type type, u32 index, bool is_float) {
if (NumComponents(type) == 1) {
return value;
}
const Id scalar_type{is_float ? ctx.F32[1] : ctx.U32[1]};
return ctx.OpCompositeExtract(scalar_type, value, index);
}
Id PromoteCoordinate(EmitContext& ctx, IR::Inst* inst, const IR::TextureInstInfo& info,
Id coords) {
if (!Needs1DPromotion(ctx.profile, info.type)) {
return coords;
}
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool has_layer{HasLayerComponent(effective_type)};
const u32 target_base_dim{BaseDimension(effective_type)};
if (target_base_dim == 0) {
return coords;
}
const IR::Type coord_type{inst->Arg(1).Type()};
const bool is_float{IsFloatCoordType(coord_type)};
const Id zero{is_float ? ctx.f32_zero_value : ctx.u32_zero_value};
const u32 total_components{NumComponents(coord_type)};
const u32 original_base_dim{total_components - (has_layer ? 1u : 0u)};
boost::container::small_vector<Id, 5> components;
components.reserve(target_base_dim + (has_layer ? 1u : 0u));
for (u32 i = 0; i < original_base_dim; ++i) {
components.push_back(ExtractComponent(ctx, coords, coord_type, i, is_float));
}
if (components.empty()) {
components.push_back(coords);
}
while (components.size() < target_base_dim) {
components.push_back(zero);
}
if (has_layer) {
const u32 layer_index{total_components == 1 ? 0u : total_components - 1u};
components.push_back(ExtractComponent(ctx, coords, coord_type, layer_index, is_float));
}
const Id vector_type{is_float ? ctx.F32[components.size()] : ctx.U32[components.size()]};
return ctx.OpCompositeConstruct(vector_type, std::span{components.data(), components.size()});
}
Id PromoteOffset(EmitContext& ctx, const IR::Value& offset, Id offset_id,
TextureType effective_type) {
const u32 required_components{BaseDimension(effective_type)};
if (required_components <= 1 || offset.IsEmpty()) {
return offset_id;
}
const IR::Type offset_type{offset.Type()};
const u32 existing_components{NumComponents(offset_type)};
if (existing_components >= required_components) {
return offset_id;
}
boost::container::small_vector<Id, 3> components;
components.reserve(required_components);
if (existing_components == 1) {
components.push_back(offset_id);
} else {
for (u32 i = 0; i < existing_components; ++i) {
components.push_back(ctx.OpCompositeExtract(ctx.S32[1], offset_id, i));
}
}
while (components.size() < required_components) {
components.push_back(ctx.SConst(0));
}
return ctx.OpCompositeConstruct(ctx.S32[required_components],
std::span{components.data(), components.size()});
}
u32 ExpectedDerivativeComponents(TextureType type) {
return BaseDimension(type);
}
class ImageOperands {
public:
[[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false;
@ -20,8 +167,10 @@ public:
[[maybe_unused]] static constexpr bool ImageFetchOffsetAllowed = false;
[[maybe_unused]] static constexpr bool ImageGradientOffsetAllowed = false;
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
Id lod, const IR::Value& offset) {
explicit ImageOperands(EmitContext& ctx, TextureType type, bool promote,
bool has_bias, bool has_lod, bool has_lod_clamp, Id lod,
const IR::Value& offset)
: texture_type{type}, needs_promotion{promote} {
if (has_bias) {
const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
Add(spv::ImageOperandsMask::Bias, bias);
@ -37,12 +186,11 @@ public:
}
}
explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, const IR::Value& offset2) {
explicit ImageOperands(EmitContext& ctx, TextureType type, bool promote,
const IR::Value& offset, const IR::Value& offset2)
: texture_type{type}, needs_promotion{promote} {
if (offset2.IsEmpty()) {
if (offset.IsEmpty()) {
return;
}
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
AddOffset(ctx, offset, ImageGatherOffsetAllowed);
return;
}
const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
@ -72,8 +220,10 @@ public:
}
}
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
u32 num_derivatives, const IR::Value& offset, Id lod_clamp) {
explicit ImageOperands(EmitContext& ctx, TextureType type, bool promote, bool has_lod_clamp,
Id derivatives, u32 num_derivatives, const IR::Value& offset,
Id lod_clamp)
: texture_type{type}, needs_promotion{promote} {
if (!Sirit::ValidId(derivatives)) {
throw LogicError("Derivatives must be present");
}
@ -83,10 +233,16 @@ public:
deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2));
deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2 + 1));
}
const u32 expected_components{needs_promotion ? ExpectedDerivativeComponents(texture_type)
: num_derivatives};
while (needs_promotion && deriv_x_accum.size() < expected_components) {
deriv_x_accum.push_back(ctx.f32_zero_value);
deriv_y_accum.push_back(ctx.f32_zero_value);
}
const Id derivatives_X{ctx.OpCompositeConstruct(
ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
ctx.F32[expected_components], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
const Id derivatives_Y{ctx.OpCompositeConstruct(
ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
ctx.F32[expected_components], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
AddOffset(ctx, offset, ImageGradientOffsetAllowed);
if (has_lod_clamp) {
@ -94,8 +250,10 @@ public:
}
}
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
const IR::Value& offset, Id lod_clamp) {
explicit ImageOperands(EmitContext& ctx, TextureType type, bool promote, bool has_lod_clamp,
Id derivatives_1, Id derivatives_2, const IR::Value& offset,
Id lod_clamp)
: texture_type{type}, needs_promotion{promote} {
if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
throw LogicError("Derivatives must be present");
}
@ -137,38 +295,62 @@ private:
if (offset.IsEmpty()) {
return;
}
const u32 required_components{BaseDimension(texture_type)};
const bool promote_offset{needs_promotion && required_components > 1};
auto build_const_offset{[&](const boost::container::small_vector<s32, 3>& values) -> Id {
switch (values.size()) {
case 1:
return ctx.SConst(values[0]);
case 2:
return ctx.SConst(values[0], values[1]);
case 3:
return ctx.SConst(values[0], values[1], values[2]);
default:
throw LogicError("Unsupported constant offset component count {}",
values.size());
}
}};
auto pad_components{[&](boost::container::small_vector<s32, 3>& components) {
while (promote_offset && components.size() < required_components) {
components.push_back(0);
}
}};
if (offset.IsImmediate()) {
Add(spv::ImageOperandsMask::ConstOffset, ctx.SConst(static_cast<s32>(offset.U32())));
boost::container::small_vector<s32, 3> components{
static_cast<s32>(offset.U32())};
pad_components(components);
Add(spv::ImageOperandsMask::ConstOffset, build_const_offset(components));
return;
}
IR::Inst* const inst{offset.InstRecursive()};
if (inst->AreAllArgsImmediates()) {
switch (inst->GetOpcode()) {
case IR::Opcode::CompositeConstructU32x2:
Add(spv::ImageOperandsMask::ConstOffset,
ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
static_cast<s32>(inst->Arg(1).U32())));
return;
case IR::Opcode::CompositeConstructU32x3:
Add(spv::ImageOperandsMask::ConstOffset,
ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
static_cast<s32>(inst->Arg(1).U32()),
static_cast<s32>(inst->Arg(2).U32())));
return;
case IR::Opcode::CompositeConstructU32x4:
Add(spv::ImageOperandsMask::ConstOffset,
ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
static_cast<s32>(inst->Arg(1).U32()),
static_cast<s32>(inst->Arg(2).U32()),
static_cast<s32>(inst->Arg(3).U32())));
case IR::Opcode::CompositeConstructU32x4: {
boost::container::small_vector<s32, 3> components;
for (u32 i = 0; i < inst->NumArgs(); ++i) {
components.push_back(static_cast<s32>(inst->Arg(i).U32()));
}
pad_components(components);
Add(spv::ImageOperandsMask::ConstOffset, build_const_offset(components));
return;
}
default:
break;
}
}
if (runtime_offset_allowed) {
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
if (!runtime_offset_allowed) {
return;
}
Id offset_id{ctx.Def(offset)};
if (promote_offset) {
offset_id = PromoteOffset(ctx, offset, offset_id, texture_type);
}
Add(spv::ImageOperandsMask::Offset, offset_id);
}
void Add(spv::ImageOperandsMask new_mask, Id value) {
@ -184,6 +366,8 @@ private:
operands.push_back(value_2);
}
TextureType texture_type{TextureType::Color2D};
bool needs_promotion{};
boost::container::static_vector<Id, 4> operands;
spv::ImageOperandsMask mask{};
};
@ -326,8 +510,7 @@ Id BitTest(EmitContext& ctx, Id mask, Id bit) {
return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
}
Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info, Id texture,
Id coords) {
Id ImageGatherSubpixelOffset(EmitContext& ctx, TextureType type, Id texture, Id coords) {
// Apply a subpixel offset of 1/512 the texel size of the texture to ensure same rounding on
// AMD hardware as on Maxwell or other Nvidia architectures.
const auto calculate_coords{[&](size_t dim) {
@ -338,7 +521,7 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
offset = ctx.OpFDiv(ctx.F32[dim], offset, ctx.OpConvertUToF(ctx.F32[dim], image_size));
return ctx.OpFAdd(ctx.F32[dim], coords, offset);
}};
switch (info.type) {
switch (type) {
case TextureType::Color2D:
case TextureType::Color2DRect:
return calculate_coords(2);
@ -350,14 +533,24 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
}
}
void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
void AddOffsetToCoordinates(EmitContext& ctx, TextureType type, bool promoted_from_1d, Id& coords,
Id offset) {
if (!Sirit::ValidId(offset)) {
return;
}
auto PadScalarOffset = [&](u32 components) {
boost::container::static_vector<Id, 3> elems;
elems.push_back(offset);
while (elems.size() < components) {
elems.push_back(ctx.u32_zero_value);
}
offset = ctx.OpCompositeConstruct(ctx.U32[components],
std::span{elems.data(), elems.size()});
};
Id result_type{};
switch (info.type) {
switch (type) {
case TextureType::Buffer:
case TextureType::Color1D: {
result_type = ctx.U32[1];
@ -368,13 +561,21 @@ void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, I
[[fallthrough]];
case TextureType::Color2D:
case TextureType::Color2DRect: {
if (promoted_from_1d) {
PadScalarOffset(2);
}
result_type = ctx.U32[2];
break;
}
case TextureType::ColorArray2D:
offset = ctx.OpCompositeConstruct(ctx.U32[3], ctx.OpCompositeExtract(ctx.U32[1], coords, 0),
ctx.OpCompositeExtract(ctx.U32[1], coords, 1),
ctx.u32_zero_value);
if (promoted_from_1d) {
PadScalarOffset(3);
} else {
offset = ctx.OpCompositeConstruct(ctx.U32[3],
ctx.OpCompositeExtract(ctx.U32[1], coords, 0),
ctx.OpCompositeExtract(ctx.U32[1], coords, 1),
ctx.u32_zero_value);
}
[[fallthrough]];
case TextureType::Color3D: {
result_type = ctx.U32[3];
@ -488,12 +689,15 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
const Id color_type{TextureColorResultType(ctx, def)};
const Id texture{Texture(ctx, info, index)};
coords = PromoteCoordinate(ctx, inst, info, coords);
Id color{};
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
const ImageOperands operands(ctx, effective_type, needs_promotion, info.has_bias != 0,
false, info.has_lod_clamp != 0, bias_lc, offset);
color = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, color_type, texture,
coords, operands.MaskOptional(), operands.Span());
@ -502,7 +706,8 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
// if the lod was explicitly zero. This may change on Turing with implicit compute
// derivatives
const Id lod{ctx.Const(0.0f)};
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
const ImageOperands operands(ctx, effective_type, needs_promotion, false, true,
info.has_lod_clamp != 0, lod, offset);
color = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture,
coords, operands.Mask(), operands.Span());
@ -514,8 +719,12 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
const Id color_type{TextureColorResultType(ctx, def)};
const ImageOperands operands(ctx, false, true, false, lod, offset);
coords = PromoteCoordinate(ctx, inst, info, coords);
const ImageOperands operands(ctx, effective_type, needs_promotion, false, true, false, lod,
offset);
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
@ -525,9 +734,12 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
coords = PromoteCoordinate(ctx, inst, info, coords);
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
const ImageOperands operands(ctx, effective_type, needs_promotion, info.has_bias != 0,
false, info.has_lod_clamp != 0, bias_lc, offset);
return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
&EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
@ -536,7 +748,8 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
// Implicit lods in compute behave on hardware as if sampling from LOD 0.
// This check is to ensure all drivers behave this way.
const Id lod{ctx.Const(0.0f)};
const ImageOperands operands(ctx, false, true, false, lod, offset);
const ImageOperands operands(ctx, effective_type, needs_promotion, false, true, false,
lod, offset);
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
@ -546,7 +759,11 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id coords, Id dref, Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, false, true, false, lod, offset);
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
coords = PromoteCoordinate(ctx, inst, info, coords);
const ImageOperands operands(ctx, effective_type, needs_promotion, false, true, false, lod,
offset);
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
@ -556,11 +773,15 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
const Id color_type{TextureColorResultType(ctx, def)};
const ImageOperands operands(ctx, offset, offset2);
coords = PromoteCoordinate(ctx, inst, info, coords);
const ImageOperands operands(ctx, effective_type, needs_promotion, offset, offset2);
const Id texture{Texture(ctx, info, index)};
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
coords = ImageGatherSubpixelOffset(ctx, effective_type, TextureImage(ctx, info, index),
coords);
}
const Id color{
Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, color_type,
@ -572,9 +793,13 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const ImageOperands operands(ctx, offset, offset2);
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
coords = PromoteCoordinate(ctx, inst, info, coords);
const ImageOperands operands(ctx, effective_type, needs_promotion, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
coords = ImageGatherSubpixelOffset(ctx, effective_type, TextureImage(ctx, info, index),
coords);
}
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
@ -586,8 +811,11 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition* def =
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
const Id result_type{def ? TextureColorResultType(ctx, *def) : ctx.F32[4]};
AddOffsetToCoordinates(ctx, info, coords, offset);
coords = PromoteCoordinate(ctx, inst, info, coords);
AddOffsetToCoordinates(ctx, effective_type, needs_promotion, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
}
@ -608,30 +836,54 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
const IR::Value& skip_mips_val) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
const Id image{TextureImage(ctx, info, index)};
const Id zero{ctx.u32_zero_value};
const bool skip_mips{skip_mips_val.U1()};
const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
const bool is_msaa{IsTextureMsaa(ctx, info)};
const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
const auto query{[&](Id type) {
return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
: ctx.OpImageQuerySize(type, image);
}};
const u32 query_components = effective_type == TextureType::Buffer
? 1u
: BaseDimension(effective_type) +
(HasLayerComponent(effective_type) ? 1u : 0u);
const Id query_type{ctx.U32[std::max(1u, query_components)]};
const Id size = uses_lod ? ctx.OpImageQuerySizeLod(query_type, image, lod)
: ctx.OpImageQuerySize(query_type, image);
const auto extract = [&](u32 index) -> Id {
if (query_components == 1) {
return size;
}
return ctx.OpCompositeExtract(ctx.U32[1], size, index);
};
switch (info.type) {
case TextureType::Color1D:
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
case TextureType::ColorArray1D:
return ctx.OpCompositeConstruct(ctx.U32[4], extract(0), zero, zero, mips());
case TextureType::ColorArray1D: {
const Id width{extract(0)};
const Id layers{needs_promotion ? extract(2) : extract(1)};
return ctx.OpCompositeConstruct(ctx.U32[4], width, layers, zero, mips());
}
case TextureType::Color2D:
case TextureType::ColorCube:
case TextureType::Color2DRect:
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
case TextureType::Color2DRect: {
const Id width{extract(0)};
const Id height{extract(1)};
return ctx.OpCompositeConstruct(ctx.U32[4], width, height, zero, mips());
}
case TextureType::ColorArray2D:
case TextureType::Color3D:
case TextureType::ColorArrayCube:
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
case TextureType::ColorArrayCube: {
const Id width{extract(0)};
const Id height{extract(1)};
const Id depth{extract(2)};
return ctx.OpCompositeConstruct(ctx.U32[4], width, height, depth, mips());
}
case TextureType::Buffer:
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
return ctx.OpCompositeConstruct(ctx.U32[4], extract(0), zero, zero, mips());
}
throw LogicError("Unspecified image type {}", info.type.Value());
}
@ -640,6 +892,7 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
const auto info{inst->Flags<IR::TextureInstInfo>()};
const Id zero{ctx.f32_zero_value};
const Id sampler{Texture(ctx, info, index)};
coords = PromoteCoordinate(ctx, inst, info, coords);
return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
zero, zero);
}
@ -648,11 +901,16 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const TextureType effective_type{GetEffectiveType(ctx, info.type)};
const bool needs_promotion{Needs1DPromotion(ctx.profile, info.type)};
const Id color_type{TextureColorResultType(ctx, def)};
coords = PromoteCoordinate(ctx, inst, info, coords);
const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
? ImageOperands(ctx, effective_type, needs_promotion,
info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), IR::Value{}, lod_clamp)
: ImageOperands(ctx, effective_type, needs_promotion,
info.has_lod_clamp != 0, derivatives,
info.num_derivatives, offset, lod_clamp);
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
@ -666,6 +924,7 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
return ctx.ConstantNull(ctx.U32[4]);
}
coords = PromoteCoordinate(ctx, inst, info, coords);
const auto [image, is_integer] = Image(ctx, index, info);
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
@ -678,6 +937,7 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = PromoteCoordinate(ctx, inst, info, coords);
const auto [image, is_integer] = Image(ctx, index, info);
if (!is_integer) {
color = ctx.OpBitcast(ctx.F32[4], color);