|
|
|
|
@@ -4,162 +4,15 @@
|
|
|
|
|
// 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;
|
|
|
|
|
@@ -167,10 +20,8 @@ public:
|
|
|
|
|
[[maybe_unused]] static constexpr bool ImageFetchOffsetAllowed = false;
|
|
|
|
|
[[maybe_unused]] static constexpr bool ImageGradientOffsetAllowed = false;
|
|
|
|
|
|
|
|
|
|
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} {
|
|
|
|
|
explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
|
|
|
|
|
Id lod, const IR::Value& offset) {
|
|
|
|
|
if (has_bias) {
|
|
|
|
|
const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
|
|
|
|
|
Add(spv::ImageOperandsMask::Bias, bias);
|
|
|
|
|
@@ -186,11 +37,12 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
explicit ImageOperands(EmitContext& ctx, TextureType type, bool promote,
|
|
|
|
|
const IR::Value& offset, const IR::Value& offset2)
|
|
|
|
|
: texture_type{type}, needs_promotion{promote} {
|
|
|
|
|
explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, const IR::Value& offset2) {
|
|
|
|
|
if (offset2.IsEmpty()) {
|
|
|
|
|
AddOffset(ctx, offset, ImageGatherOffsetAllowed);
|
|
|
|
|
if (offset.IsEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
|
|
|
|
|
@@ -220,10 +72,8 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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} {
|
|
|
|
|
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
|
|
|
|
|
u32 num_derivatives, const IR::Value& offset, Id lod_clamp) {
|
|
|
|
|
if (!Sirit::ValidId(derivatives)) {
|
|
|
|
|
throw LogicError("Derivatives must be present");
|
|
|
|
|
}
|
|
|
|
|
@@ -233,16 +83,10 @@ 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[expected_components], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
|
|
|
|
|
ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
|
|
|
|
|
const Id derivatives_Y{ctx.OpCompositeConstruct(
|
|
|
|
|
ctx.F32[expected_components], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
|
|
|
|
|
ctx.F32[num_derivatives], 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) {
|
|
|
|
|
@@ -250,10 +94,8 @@ public:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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} {
|
|
|
|
|
explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
|
|
|
|
|
const IR::Value& offset, Id lod_clamp) {
|
|
|
|
|
if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
|
|
|
|
|
throw LogicError("Derivatives must be present");
|
|
|
|
|
}
|
|
|
|
|
@@ -295,62 +137,38 @@ 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()) {
|
|
|
|
|
boost::container::small_vector<s32, 3> components{
|
|
|
|
|
static_cast<s32>(offset.U32())};
|
|
|
|
|
pad_components(components);
|
|
|
|
|
Add(spv::ImageOperandsMask::ConstOffset, build_const_offset(components));
|
|
|
|
|
Add(spv::ImageOperandsMask::ConstOffset, ctx.SConst(static_cast<s32>(offset.U32())));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
IR::Inst* const inst{offset.InstRecursive()};
|
|
|
|
|
if (inst->AreAllArgsImmediates()) {
|
|
|
|
|
switch (inst->GetOpcode()) {
|
|
|
|
|
case IR::Opcode::CompositeConstructU32x2:
|
|
|
|
|
case IR::Opcode::CompositeConstructU32x3:
|
|
|
|
|
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));
|
|
|
|
|
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())));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!runtime_offset_allowed) {
|
|
|
|
|
return;
|
|
|
|
|
if (runtime_offset_allowed) {
|
|
|
|
|
Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
@@ -366,8 +184,6 @@ private:
|
|
|
|
|
operands.push_back(value_2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextureType texture_type{TextureType::Color2D};
|
|
|
|
|
bool needs_promotion{};
|
|
|
|
|
boost::container::static_vector<Id, 4> operands;
|
|
|
|
|
spv::ImageOperandsMask mask{};
|
|
|
|
|
};
|
|
|
|
|
@@ -510,7 +326,8 @@ Id BitTest(EmitContext& ctx, Id mask, Id bit) {
|
|
|
|
|
return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Id ImageGatherSubpixelOffset(EmitContext& ctx, TextureType type, Id texture, Id coords) {
|
|
|
|
|
Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info, 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) {
|
|
|
|
|
@@ -521,7 +338,7 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, TextureType type, Id texture, Id
|
|
|
|
|
offset = ctx.OpFDiv(ctx.F32[dim], offset, ctx.OpConvertUToF(ctx.F32[dim], image_size));
|
|
|
|
|
return ctx.OpFAdd(ctx.F32[dim], coords, offset);
|
|
|
|
|
}};
|
|
|
|
|
switch (type) {
|
|
|
|
|
switch (info.type) {
|
|
|
|
|
case TextureType::Color2D:
|
|
|
|
|
case TextureType::Color2DRect:
|
|
|
|
|
return calculate_coords(2);
|
|
|
|
|
@@ -533,24 +350,14 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, TextureType type, Id texture, Id
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddOffsetToCoordinates(EmitContext& ctx, TextureType type, bool promoted_from_1d, Id& coords,
|
|
|
|
|
void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, 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 (type) {
|
|
|
|
|
switch (info.type) {
|
|
|
|
|
case TextureType::Buffer:
|
|
|
|
|
case TextureType::Color1D: {
|
|
|
|
|
result_type = ctx.U32[1];
|
|
|
|
|
@@ -561,21 +368,13 @@ void AddOffsetToCoordinates(EmitContext& ctx, TextureType type, bool promoted_fr
|
|
|
|
|
[[fallthrough]];
|
|
|
|
|
case TextureType::Color2D:
|
|
|
|
|
case TextureType::Color2DRect: {
|
|
|
|
|
if (promoted_from_1d) {
|
|
|
|
|
PadScalarOffset(2);
|
|
|
|
|
}
|
|
|
|
|
result_type = ctx.U32[2];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TextureType::ColorArray2D:
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
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];
|
|
|
|
|
@@ -689,15 +488,12 @@ 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, effective_type, needs_promotion, info.has_bias != 0,
|
|
|
|
|
false, info.has_lod_clamp != 0, bias_lc, offset);
|
|
|
|
|
const ImageOperands operands(ctx, 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());
|
|
|
|
|
@@ -706,8 +502,7 @@ 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, effective_type, needs_promotion, false, true,
|
|
|
|
|
info.has_lod_clamp != 0, lod, offset);
|
|
|
|
|
const ImageOperands operands(ctx, 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());
|
|
|
|
|
@@ -719,12 +514,8 @@ 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)};
|
|
|
|
|
coords = PromoteCoordinate(ctx, inst, info, coords);
|
|
|
|
|
const ImageOperands operands(ctx, effective_type, needs_promotion, false, true, false, lod,
|
|
|
|
|
offset);
|
|
|
|
|
const ImageOperands operands(ctx, 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())};
|
|
|
|
|
@@ -734,12 +525,9 @@ 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, effective_type, needs_promotion, info.has_bias != 0,
|
|
|
|
|
false, info.has_lod_clamp != 0, bias_lc, offset);
|
|
|
|
|
const ImageOperands operands(ctx, 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(),
|
|
|
|
|
@@ -748,8 +536,7 @@ 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, effective_type, needs_promotion, false, true, false,
|
|
|
|
|
lod, offset);
|
|
|
|
|
const ImageOperands operands(ctx, 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());
|
|
|
|
|
@@ -759,11 +546,7 @@ 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 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);
|
|
|
|
|
const ImageOperands operands(ctx, 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());
|
|
|
|
|
@@ -773,15 +556,11 @@ 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)};
|
|
|
|
|
coords = PromoteCoordinate(ctx, inst, info, coords);
|
|
|
|
|
const ImageOperands operands(ctx, effective_type, needs_promotion, offset, offset2);
|
|
|
|
|
const ImageOperands operands(ctx, offset, offset2);
|
|
|
|
|
const Id texture{Texture(ctx, info, index)};
|
|
|
|
|
if (ctx.profile.need_gather_subpixel_offset) {
|
|
|
|
|
coords = ImageGatherSubpixelOffset(ctx, effective_type, TextureImage(ctx, info, index),
|
|
|
|
|
coords);
|
|
|
|
|
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
|
|
|
|
}
|
|
|
|
|
const Id color{
|
|
|
|
|
Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, color_type,
|
|
|
|
|
@@ -793,13 +572,9 @@ 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 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);
|
|
|
|
|
const ImageOperands operands(ctx, offset, offset2);
|
|
|
|
|
if (ctx.profile.need_gather_subpixel_offset) {
|
|
|
|
|
coords = ImageGatherSubpixelOffset(ctx, effective_type, TextureImage(ctx, info, index),
|
|
|
|
|
coords);
|
|
|
|
|
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
|
|
|
|
}
|
|
|
|
|
return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
|
|
|
|
|
ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
|
|
|
|
|
@@ -811,11 +586,8 @@ 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]};
|
|
|
|
|
coords = PromoteCoordinate(ctx, inst, info, coords);
|
|
|
|
|
AddOffsetToCoordinates(ctx, effective_type, needs_promotion, coords, offset);
|
|
|
|
|
AddOffsetToCoordinates(ctx, info, coords, offset);
|
|
|
|
|
if (info.type == TextureType::Buffer) {
|
|
|
|
|
lod = Id{};
|
|
|
|
|
}
|
|
|
|
|
@@ -836,54 +608,30 @@ 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 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);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const auto query{[&](Id type) {
|
|
|
|
|
return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
|
|
|
|
|
: ctx.OpImageQuerySize(type, image);
|
|
|
|
|
}};
|
|
|
|
|
switch (info.type) {
|
|
|
|
|
case TextureType::Color1D:
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
|
|
|
|
|
case TextureType::ColorArray1D:
|
|
|
|
|
case TextureType::Color2D:
|
|
|
|
|
case TextureType::ColorCube:
|
|
|
|
|
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::Color2DRect:
|
|
|
|
|
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
|
|
|
|
|
case TextureType::ColorArray2D:
|
|
|
|
|
case TextureType::Color3D:
|
|
|
|
|
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::ColorArrayCube:
|
|
|
|
|
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
|
|
|
|
|
case TextureType::Buffer:
|
|
|
|
|
return ctx.OpCompositeConstruct(ctx.U32[4], extract(0), zero, zero, mips());
|
|
|
|
|
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
|
|
|
|
|
}
|
|
|
|
|
throw LogicError("Unspecified image type {}", info.type.Value());
|
|
|
|
|
}
|
|
|
|
|
@@ -892,7 +640,6 @@ 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);
|
|
|
|
|
}
|
|
|
|
|
@@ -901,16 +648,11 @@ 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, 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,
|
|
|
|
|
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
|
|
|
|
ctx.Def(offset), {}, lod_clamp)
|
|
|
|
|
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
|
|
|
|
info.num_derivatives, offset, lod_clamp);
|
|
|
|
|
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
|
|
|
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
|
|
|
|
|
@@ -924,7 +666,6 @@ 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,
|
|
|
|
|
@@ -937,7 +678,6 @@ 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);
|
|
|
|
|
|