mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
shader: emit native texel offsets using sample execution context
Co-authored-by: netwarex <mail@pety.me>
This commit is contained in:
@@ -323,25 +323,25 @@ uint32_t QueryDimensions(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
|||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t PackedOffset(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR::Inst& address,
|
uint32_t PackedOffset(ValueEmitContext& ctx, IR::Value value, ImageDimension dimension,
|
||||||
const ImageSampleLayout& layout, ImageDimension dimension) {
|
uint32_t& mask) {
|
||||||
const auto components = ImageDimensionInfoFor(dimension).spatial_components;
|
const auto components = ImageDimensionInfoFor(dimension).spatial_components;
|
||||||
const auto zero = ConstantI32(ctx.state, 0);
|
uint32_t values[3] {};
|
||||||
if (layout.offset == NoImageComponent || mem.image_address_components <= layout.offset) {
|
if (value.IsImmediate()) {
|
||||||
if (components == 1u) return zero;
|
// RDNA2 packs signed six-bit texel offsets into the low bits of each byte.
|
||||||
const auto result = ctx.state.builder.AllocateId();
|
for (uint32_t index = 0; index < components; index++) {
|
||||||
if (components == 3u) {
|
const auto bits = (value.U32() >> (index * 8u)) & 0x3fu;
|
||||||
ctx.state.builder.AddFunction(spv::OpCompositeConstruct, TypeI32Vector(ctx.state, 3),
|
values[index] = ConstantI32(ctx.state, static_cast<int32_t>(bits ^ 0x20u) - 0x20);
|
||||||
result, zero, zero, zero);
|
|
||||||
} else {
|
|
||||||
ctx.state.builder.AddFunction(spv::OpCompositeConstruct, TypeI32Vector(ctx.state, 2),
|
|
||||||
result, zero, zero);
|
|
||||||
}
|
}
|
||||||
return result;
|
mask |= spv::ImageOperandsConstOffsetMask;
|
||||||
|
return components == 1u
|
||||||
|
? values[0]
|
||||||
|
: ctx.state.builder.Constant(spv::OpConstantComposite,
|
||||||
|
TypeI32Vector(ctx.state, components),
|
||||||
|
std::span(values).first(components));
|
||||||
}
|
}
|
||||||
const auto packed = Unary(ctx.state, spv::OpBitcast, TypeI32(ctx.state),
|
mask |= spv::ImageOperandsOffsetMask;
|
||||||
AddressU32(ctx, mem, address, layout.offset));
|
const auto packed = Unary(ctx.state, spv::OpBitcast, TypeI32(ctx.state), ctx.Def(value));
|
||||||
uint32_t values[3] = {zero, zero, zero};
|
|
||||||
for (uint32_t index = 0; index < components; index++) {
|
for (uint32_t index = 0; index < components; index++) {
|
||||||
values[index] = ctx.state.builder.AllocateId();
|
values[index] = ctx.state.builder.AllocateId();
|
||||||
ctx.state.builder.AddFunction(spv::OpBitFieldSExtract, TypeI32(ctx.state), values[index],
|
ctx.state.builder.AddFunction(spv::OpBitFieldSExtract, TypeI32(ctx.state), values[index],
|
||||||
@@ -350,13 +350,8 @@ uint32_t PackedOffset(ValueEmitContext& ctx, const IR::MemoryInfo& mem, const IR
|
|||||||
}
|
}
|
||||||
if (components == 1u) return values[0];
|
if (components == 1u) return values[0];
|
||||||
const auto result = ctx.state.builder.AllocateId();
|
const auto result = ctx.state.builder.AllocateId();
|
||||||
if (components == 3u) {
|
ctx.state.builder.AddFunction(spv::OpCompositeConstruct, TypeI32Vector(ctx.state, components),
|
||||||
ctx.state.builder.AddFunction(spv::OpCompositeConstruct, TypeI32Vector(ctx.state, 3),
|
result, std::span(values).first(components));
|
||||||
result, values[0], values[1], values[2]);
|
|
||||||
} else {
|
|
||||||
ctx.state.builder.AddFunction(spv::OpCompositeConstruct, TypeI32Vector(ctx.state, 2),
|
|
||||||
result, values[0], values[1]);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,6 +789,12 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto layout = Layout(mem);
|
const auto layout = Layout(mem);
|
||||||
const auto numeric_class = image.numeric_class;
|
const auto numeric_class = image.numeric_class;
|
||||||
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
const bool dref = HasFlag(mem, Decoder::ImageSampleFlagCompare);
|
||||||
|
IR::Value offset_value;
|
||||||
|
if (layout.offset != NoImageComponent) {
|
||||||
|
const auto value = address->Arg(layout.offset).Resolve();
|
||||||
|
offset_value = IR::ResolveActiveU32(value, inst.Arg(inst.NumArgs() - 1u).Resolve());
|
||||||
|
if (offset_value.IsEmpty()) offset_value = value;
|
||||||
|
}
|
||||||
if (dref && state.program.info.images[mem.resource].conversion_format !=
|
if (dref && state.program.info.images[mem.resource].conversion_format !=
|
||||||
Prospero::BufferFormat::kInvalid) {
|
Prospero::BufferFormat::kInvalid) {
|
||||||
ctx.Fail(inst, "uses depth comparison with a converted image");
|
ctx.Fail(inst, "uses depth comparison with a converted image");
|
||||||
@@ -843,8 +844,7 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
operand_mask = spv::ImageOperandsConstOffsetsMask;
|
operand_mask = spv::ImageOperandsConstOffsetsMask;
|
||||||
offset = HorizontalOffsets(state, dimension);
|
offset = HorizontalOffsets(state, dimension);
|
||||||
} else if (layout.offset != NoImageComponent) {
|
} else if (layout.offset != NoImageComponent) {
|
||||||
operand_mask = spv::ImageOperandsOffsetMask;
|
offset = PackedOffset(ctx, offset_value, dimension, operand_mask);
|
||||||
offset = PackedOffset(ctx, mem, *address, layout, dimension);
|
|
||||||
}
|
}
|
||||||
const auto EmitMip = [&](uint32_t mip) {
|
const auto EmitMip = [&](uint32_t mip) {
|
||||||
const auto sampled = MakeSampledImage(state, resource, sampler_id, mip, array_index);
|
const auto sampled = MakeSampledImage(state, resource, sampler_id, mip, array_index);
|
||||||
@@ -886,7 +886,7 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
dref_value = AddressF32(ctx, mem, *address, layout.dref);
|
dref_value = AddressF32(ctx, mem, *address, layout.dref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::array<uint32_t, 3> operands {};
|
std::array<uint32_t, 4> operands {};
|
||||||
size_t operand_count = 0;
|
size_t operand_count = 0;
|
||||||
if (HasFlag(mem, Decoder::ImageSampleFlagDerivative)) {
|
if (HasFlag(mem, Decoder::ImageSampleFlagDerivative)) {
|
||||||
operands[0] = spv::ImageOperandsGradMask;
|
operands[0] = spv::ImageOperandsGradMask;
|
||||||
@@ -916,9 +916,20 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
state, ctx.Def(handle->Arg(0)), static_cast<uint32_t>(samplers.size()),
|
state, ctx.Def(handle->Arg(0)), static_cast<uint32_t>(samplers.size()),
|
||||||
TypeU32(state), [&](uint32_t ordinal) { return ConstantU32(state, samplers[ordinal]); });
|
TypeU32(state), [&](uint32_t ordinal) { return ConstantU32(state, samplers[ordinal]); });
|
||||||
}
|
}
|
||||||
|
if (layout.offset != NoImageComponent &&
|
||||||
|
!offset_value.IsImmediate()) {
|
||||||
|
ctx.Fail(inst, "has a nonconstant sample texel offset");
|
||||||
|
}
|
||||||
const auto sampler_id = LoadSamplerDescriptor(state, mem.sampler, sampler_index);
|
const auto sampler_id = LoadSamplerDescriptor(state, mem.sampler, sampler_index);
|
||||||
const auto EmitSample = [&](uint32_t resource, uint32_t array_index) {
|
const auto EmitSample = [&](uint32_t resource, uint32_t array_index) {
|
||||||
const auto& candidate = state.program.info.images[resource];
|
const auto& candidate = state.program.info.images[resource];
|
||||||
|
auto sample_operands = operands;
|
||||||
|
auto sample_operand_count = operand_count;
|
||||||
|
if (layout.offset != NoImageComponent) {
|
||||||
|
sample_operand_count = std::max(size_t{1}, sample_operand_count);
|
||||||
|
sample_operands[sample_operand_count++] = PackedOffset(
|
||||||
|
ctx, offset_value, candidate.dimension, sample_operands[0]);
|
||||||
|
}
|
||||||
const auto coord =
|
const auto coord =
|
||||||
CoordF32(ctx, mem, *address, layout.coord,
|
CoordF32(ctx, mem, *address, layout.coord,
|
||||||
ImageDimensionInfoFor(candidate.dimension).coordinate_components,
|
ImageDimensionInfoFor(candidate.dimension).coordinate_components,
|
||||||
@@ -928,7 +939,7 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto sample = state.builder.AllocateId();
|
const auto sample = state.builder.AllocateId();
|
||||||
state.builder.AddFunction(opcode, result_type, sample, sampled, coord,
|
state.builder.AddFunction(opcode, result_type, sample, sampled, coord,
|
||||||
std::span(&dref_value, dref ? 1u : 0u),
|
std::span(&dref_value, dref ? 1u : 0u),
|
||||||
std::span<const uint32_t>(operands).first(operand_count));
|
std::span<const uint32_t>(sample_operands).first(sample_operand_count));
|
||||||
return sample;
|
return sample;
|
||||||
};
|
};
|
||||||
auto result = EmitImageAccess(ctx, inst, result_type, EmitSample);
|
auto result = EmitImageAccess(ctx, inst, result_type, EmitSample);
|
||||||
|
|||||||
@@ -687,7 +687,8 @@ void Translator::IMAGE_SAMPLE(const Decoder::Instruction& inst) {
|
|||||||
const auto resource = GetImageResource(memory);
|
const auto resource = GetImageResource(memory);
|
||||||
const auto sampler = GetSamplerResource(memory);
|
const auto sampler = GetSamplerResource(memory);
|
||||||
const auto address = MakeImageAddress(inst, MemorySourceAt(inst, 0));
|
const auto address = MakeImageAddress(inst, MemorySourceAt(inst, 0));
|
||||||
const auto result = ir.Emit(IR::ValueOpcode::ImageSampleRaw, {resource, sampler, address},
|
const auto result = ir.Emit(IR::ValueOpcode::ImageSampleRaw,
|
||||||
|
{resource, sampler, address, ir.GetExec()},
|
||||||
AddMemoryInfo(memory, inst.pc));
|
AddMemoryInfo(memory, inst.pc));
|
||||||
const bool dref = (memory.image_sample_flags & Decoder::ImageSampleFlagCompare) != 0u;
|
const bool dref = (memory.image_sample_flags & Decoder::ImageSampleFlagCompare) != 0u;
|
||||||
if (dref && memory.data_bits != 16u) {
|
if (dref && memory.data_bits != 16u) {
|
||||||
@@ -714,7 +715,7 @@ void Translator::IMAGE_GATHER(const Decoder::Instruction& inst) {
|
|||||||
has_lod ? resource.Instruction()->Arg(1) : IR::Value(0u),
|
has_lod ? resource.Instruction()->Arg(1) : IR::Value(0u),
|
||||||
has_lod ? resource.Instruction()->Arg(3) : IR::Value(0u),
|
has_lod ? resource.Instruction()->Arg(3) : IR::Value(0u),
|
||||||
has_lod ? sampler.Instruction()->Arg(1) : IR::Value(0u),
|
has_lod ? sampler.Instruction()->Arg(1) : IR::Value(0u),
|
||||||
has_lod ? sampler.Instruction()->Arg(2) : IR::Value(0u)},
|
has_lod ? sampler.Instruction()->Arg(2) : IR::Value(0u), ir.GetExec()},
|
||||||
AddMemoryInfo(memory, inst.pc));
|
AddMemoryInfo(memory, inst.pc));
|
||||||
for (uint32_t index = 0; index < memory.data_dwords; index++) {
|
for (uint32_t index = 0; index < memory.data_dwords; index++) {
|
||||||
WriteOperand(OffsetOperand(inst.dst, index),
|
WriteOperand(OffsetOperand(inst.dst, index),
|
||||||
|
|||||||
@@ -317,8 +317,8 @@ VALUE_OPCODE(BvhIntersect, U32x4, U32x4, ImageAddress, U1)
|
|||||||
VALUE_OPCODE(ImageQueryLod, U32x4, ImageResource, SamplerResource, ImageAddress)
|
VALUE_OPCODE(ImageQueryLod, U32x4, ImageResource, SamplerResource, ImageAddress)
|
||||||
VALUE_OPCODE(ImageRead, U32x4, ImageResource, ImageAddress, U1)
|
VALUE_OPCODE(ImageRead, U32x4, ImageResource, ImageAddress, U1)
|
||||||
VALUE_OPCODE(ImageWrite, Void, ImageResource, ImageAddress, U32x4, U1)
|
VALUE_OPCODE(ImageWrite, Void, ImageResource, ImageAddress, U32x4, U1)
|
||||||
VALUE_OPCODE(ImageSampleRaw, U32x4, ImageResource, SamplerResource, ImageAddress)
|
VALUE_OPCODE(ImageSampleRaw, U32x4, ImageResource, SamplerResource, ImageAddress, U1)
|
||||||
VALUE_OPCODE(ImageGatherRaw, U32x4, ImageResource, SamplerResource, ImageAddress, U32, U32, U32, U32)
|
VALUE_OPCODE(ImageGatherRaw, U32x4, ImageResource, SamplerResource, ImageAddress, U32, U32, U32, U32, U1)
|
||||||
VALUE_OPCODE(ImageAtomicCompareSwap32, U32, ImageResource, ImageAddress, U32, U32, U1)
|
VALUE_OPCODE(ImageAtomicCompareSwap32, U32, ImageResource, ImageAddress, U32, U32, U1)
|
||||||
VALUE_OPCODE(ImageAtomicSwap32, U32, ImageResource, ImageAddress, U32, U1)
|
VALUE_OPCODE(ImageAtomicSwap32, U32, ImageResource, ImageAddress, U32, U1)
|
||||||
VALUE_OPCODE(ImageAtomicSwap64, U64, ImageResource, ImageAddress, U64, U1)
|
VALUE_OPCODE(ImageAtomicSwap64, U64, ImageResource, ImageAddress, U64, U1)
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ MakeIndirectImageFixture(bool malformed, uint32_t table_stride = 32, uint32_t ta
|
|||||||
sample.kind = ResourceKind::Image;
|
sample.kind = ResourceKind::Image;
|
||||||
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
const auto sampled = fixture->Emit(ValueOpcode::ImageSampleRaw,
|
const auto sampled = fixture->Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{image, sampler, fixture->ImageAddress()},
|
{image, sampler, fixture->ImageAddress(), Value(true)},
|
||||||
fixture->AddMemory(sample, 0x10f0));
|
fixture->AddMemory(sample, 0x10f0));
|
||||||
const auto sampled_x =
|
const auto sampled_x =
|
||||||
fixture->Emit(ValueOpcode::CompositeExtractU32x4, {sampled, Value(0u)});
|
fixture->Emit(ValueOpcode::CompositeExtractU32x4, {sampled, Value(0u)});
|
||||||
@@ -486,7 +486,8 @@ void TestWaterfallImageTable() {
|
|||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
const auto value = fixture->Emit(ValueOpcode::ImageSampleRaw,
|
const auto value = fixture->Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{image, sampler, fixture->ImageAddress()}, fixture->AddMemory(memory, 0x92cu), block);
|
{image, sampler, fixture->ImageAddress(), block == entry ? active : guard},
|
||||||
|
fixture->AddMemory(memory, 0x92cu), block);
|
||||||
fixture->Emit(ValueOpcode::ReferenceU32,
|
fixture->Emit(ValueOpcode::ReferenceU32,
|
||||||
{fixture->Emit(ValueOpcode::CompositeExtractU32x4, {value, Value(0u)}, 0, block)}, 0, block);
|
{fixture->Emit(ValueOpcode::CompositeExtractU32x4, {value, Value(0u)}, 0, block)}, 0, block);
|
||||||
};
|
};
|
||||||
@@ -641,7 +642,7 @@ void TestGuardedDirectImageTable() {
|
|||||||
MemoryInfo memory;
|
MemoryInfo memory;
|
||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x128));
|
fixture.AddMemory(memory, 0x128));
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
const auto source = fixture.program.info.images[0].source;
|
const auto source = fixture.program.info.images[0].source;
|
||||||
@@ -989,7 +990,7 @@ void TestBoundedComputeImageLoop() {
|
|||||||
sample.kind = ResourceKind::Image;
|
sample.kind = ResourceKind::Image;
|
||||||
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{image, sampler, fixture.ImageAddress()},
|
{image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(sample, 0x29c));
|
fixture.AddMemory(sample, 0x29c));
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
const auto source = fixture.program.info.images[0].source;
|
const auto source = fixture.program.info.images[0].source;
|
||||||
@@ -1325,7 +1326,9 @@ void TestUniformizedMaterialImageKeys() {
|
|||||||
MemoryInfo image_memory;
|
MemoryInfo image_memory;
|
||||||
image_memory.kind = ResourceKind::Image;
|
image_memory.kind = ResourceKind::Image;
|
||||||
image_memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
image_memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
const auto sampled = fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, address},
|
const auto sampled = fixture.Emit(ValueOpcode::ImageSampleRaw,
|
||||||
|
{image, sampler, address,
|
||||||
|
variant == Variant::EmptyFirstEntry ? material_guard : sample_guard},
|
||||||
fixture.AddMemory(image_memory, 0x1aecu), sample);
|
fixture.AddMemory(image_memory, 0x1aecu), sample);
|
||||||
if (first_lane) {
|
if (first_lane) {
|
||||||
const auto component = fixture.Emit(
|
const auto component = fixture.Emit(
|
||||||
@@ -1477,7 +1480,7 @@ void TestImageDescriptorFields() {
|
|||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
memory.image_r128 = r128;
|
memory.image_r128 = r128;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x80));
|
fixture.AddMemory(memory, 0x80));
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
auto plan = ExtractResourcePlan(fixture.program);
|
auto plan = ExtractResourcePlan(fixture.program);
|
||||||
@@ -1571,7 +1574,7 @@ void TestUniformScalarBufferImage() {
|
|||||||
sample.kind = ResourceKind::Image;
|
sample.kind = ResourceKind::Image;
|
||||||
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{image, sampler, fixture.ImageAddress()}, fixture.AddMemory(sample, 0x228));
|
{image, sampler, fixture.ImageAddress(), Value(true)}, fixture.AddMemory(sample, 0x228));
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
const auto plan = ExtractResourcePlan(fixture.program);
|
const auto plan = ExtractResourcePlan(fixture.program);
|
||||||
|
|
||||||
@@ -1866,7 +1869,7 @@ void TestImagesSamplersAndAliases() {
|
|||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
memory.image_sample_flags = sample_flags;
|
memory.image_sample_flags = sample_flags;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, image_address},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, image_address, Value(true)},
|
||||||
fixture.AddMemory(memory, pc));
|
fixture.AddMemory(memory, pc));
|
||||||
return std::pair{image, sampler};
|
return std::pair{image, sampler};
|
||||||
};
|
};
|
||||||
@@ -1938,14 +1941,14 @@ void TestSampleAdjustSamplerScratch() {
|
|||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
memory.image_sample_flags = Decoder::ImageSampleFlagAdjust;
|
memory.image_sample_flags = Decoder::ImageSampleFlagAdjust;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{image, sampler, fixture.ImageAddress()},
|
{image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x1ec));
|
fixture.AddMemory(memory, 0x1ec));
|
||||||
// PPSA24156's zero border payload leaves the reserved scratch shift as
|
// PPSA24156's zero border payload leaves the reserved scratch shift as
|
||||||
// the entire DWORD after the frontend folds its OR with zero.
|
// the entire DWORD after the frontend folds its OR with zero.
|
||||||
const auto shift_sampler = fixture.Sampler(
|
const auto shift_sampler = fixture.Sampler(
|
||||||
{Value(0x36u), Value(0xfff000u), Value(0x02500000u), scratch}, 0x200);
|
{Value(0x36u), Value(0xfff000u), Value(0x02500000u), scratch}, 0x200);
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
fixture.Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{image, shift_sampler, fixture.ImageAddress()},
|
{image, shift_sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x200));
|
fixture.AddMemory(memory, 0x200));
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
auto plan = ExtractResourcePlan(fixture.program);
|
auto plan = ExtractResourcePlan(fixture.program);
|
||||||
@@ -1994,7 +1997,7 @@ void TestSampleAdjustSamplerScratch() {
|
|||||||
rejected_memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
rejected_memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
rejected_memory.image_sample_flags = flags;
|
rejected_memory.image_sample_flags = flags;
|
||||||
rejected.Emit(ValueOpcode::ImageSampleRaw,
|
rejected.Emit(ValueOpcode::ImageSampleRaw,
|
||||||
{rejected_image, rejected_sampler, rejected.ImageAddress()},
|
{rejected_image, rejected_sampler, rejected.ImageAddress(), Value(true)},
|
||||||
rejected.AddMemory(rejected_memory, 0x200));
|
rejected.AddMemory(rejected_memory, 0x200));
|
||||||
|
|
||||||
CheckFatal([&] { rejected.PlanAndTrack(); },
|
CheckFatal([&] { rejected.PlanAndTrack(); },
|
||||||
@@ -2112,7 +2115,7 @@ void TestGatherLodSamplerValidation() {
|
|||||||
fixture.Emit(ValueOpcode::ImageGatherRaw,
|
fixture.Emit(ValueOpcode::ImageGatherRaw,
|
||||||
{fixture.Image(image_words), fixture.Sampler(sampler_words),
|
{fixture.Image(image_words), fixture.Sampler(sampler_words),
|
||||||
fixture.ImageAddress(), image_words[1], image_words[3],
|
fixture.ImageAddress(), image_words[1], image_words[3],
|
||||||
sampler_words[1], sampler_words[2]},
|
sampler_words[1], sampler_words[2], Value(true)},
|
||||||
fixture.AddMemory(memory, i * 4));
|
fixture.AddMemory(memory, i * 4));
|
||||||
}
|
}
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
@@ -2514,7 +2517,7 @@ ResourcePlan ConditionalSamplerPlan(bool diamond, bool reverse, bool reverse_phi
|
|||||||
sample.kind = ResourceKind::Image;
|
sample.kind = ResourceKind::Image;
|
||||||
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
sample.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
const auto result =
|
const auto result =
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, address},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, address, Value(true)},
|
||||||
fixture.AddMemory(sample, 0x4d4));
|
fixture.AddMemory(sample, 0x4d4));
|
||||||
fixture.Emit(ValueOpcode::ReferenceU32,
|
fixture.Emit(ValueOpcode::ReferenceU32,
|
||||||
{fixture.Emit(ValueOpcode::CompositeExtractU32x4,
|
{fixture.Emit(ValueOpcode::CompositeExtractU32x4,
|
||||||
@@ -2619,7 +2622,7 @@ void TestFiniteImagePhiCycle() {
|
|||||||
MemoryInfo memory;
|
MemoryInfo memory;
|
||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 4));
|
fixture.AddMemory(memory, 4));
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
auto plan = ExtractResourcePlan(fixture.program);
|
auto plan = ExtractResourcePlan(fixture.program);
|
||||||
@@ -2685,12 +2688,12 @@ void TestFiniteInlineSampler() {
|
|||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
if (format == F::k32Float)
|
if (format == F::k32Float)
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, previous, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, previous, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x440u));
|
fixture.AddMemory(memory, 0x440u));
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x1dd0u));
|
fixture.AddMemory(memory, 0x1dd0u));
|
||||||
if (format == F::k32Float)
|
if (format == F::k32Float)
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, direct, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, direct, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x1dd8u));
|
fixture.AddMemory(memory, 0x1dd8u));
|
||||||
}
|
}
|
||||||
fixture.PlanAndTrack();
|
fixture.PlanAndTrack();
|
||||||
@@ -2764,7 +2767,7 @@ void TestFiniteInlineSampler() {
|
|||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
memory.image_sample_flags = flags;
|
memory.image_sample_flags = flags;
|
||||||
adjusted.Emit(ValueOpcode::ImageSampleRaw, {image, shared, adjusted.ImageAddress()},
|
adjusted.Emit(ValueOpcode::ImageSampleRaw, {image, shared, adjusted.ImageAddress(), Value(true)},
|
||||||
adjusted.AddMemory(memory, 0x1dd0u));
|
adjusted.AddMemory(memory, 0x1dd0u));
|
||||||
}
|
}
|
||||||
CheckFatal([&] { adjusted.PlanAndTrack(); }, "not a valid runtime value",
|
CheckFatal([&] { adjusted.PlanAndTrack(); }, "not a valid runtime value",
|
||||||
@@ -2817,7 +2820,7 @@ void TestFiniteImageBitScanSentinel() {
|
|||||||
MemoryInfo memory;
|
MemoryInfo memory;
|
||||||
memory.kind = ResourceKind::Image;
|
memory.kind = ResourceKind::Image;
|
||||||
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
memory.image_dimension = Decoder::ImageDimension::Dim2D;
|
||||||
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress()},
|
fixture.Emit(ValueOpcode::ImageSampleRaw, {image, sampler, fixture.ImageAddress(), Value(true)},
|
||||||
fixture.AddMemory(memory, 4));
|
fixture.AddMemory(memory, 4));
|
||||||
if (!nonzero) {
|
if (!nonzero) {
|
||||||
CheckFatal([&] { fixture.PlanAndTrack(); }, "not a valid runtime value",
|
CheckFatal([&] { fixture.PlanAndTrack(); }, "not a valid runtime value",
|
||||||
|
|||||||
@@ -34960,12 +34960,11 @@ TestCase ImageSampleOpcodeAliasUsesNormalCoords() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu() {
|
template <u32 wave_size> TestCase ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
AppendVMovU32(&code, 20,
|
AppendVMovU32(&code, 20, 1); // +1 X remains a full dword with A16 coordinates.
|
||||||
1); // Non-constant +1 X offset is not a SPIR-V ConstOffset.
|
|
||||||
AppendVMovLiteral(&code, 21, 0x36003900u); // x=0.625, y=0.375 packed as f16.
|
AppendVMovLiteral(&code, 21, 0x36003900u); // x=0.625, y=0.375 packed as f16.
|
||||||
AppendVMovU32(&code, 22, 0);
|
AppendVMovU32(&code, 22, 0);
|
||||||
code.push_back(EncodeMimg0(0x30, 0xf));
|
code.push_back(EncodeMimg0(0x30, 0xf));
|
||||||
@@ -34976,18 +34975,63 @@ TestCase ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu() {
|
|||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
|
|
||||||
auto image = MakeRgbaImage(4, 4);
|
auto image = MakeRgbaImage(4, 4);
|
||||||
SetRgbaPixel(&image, 4, 2, 1, 0x3f800000u, 0x40000000u, 0x40400000u,
|
SetRgbaPixel(&image, 4, 2, 1, 0x3f000000u, 0, 0, 0);
|
||||||
|
SetRgbaPixel(&image, 4, 3, 1, 0x3f800000u, 0x40000000u, 0x40400000u,
|
||||||
0x40800000u);
|
0x40800000u);
|
||||||
|
|
||||||
TestCase test;
|
TestCase test;
|
||||||
test.name = "ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu";
|
test.name = wave_size == 32 ? "ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu32"
|
||||||
|
: "ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu64";
|
||||||
test.code = code;
|
test.code = code;
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.has_compute_info = true;
|
||||||
test.expected = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u};
|
test.expected = {0x3f800000u, 0x40000000u, 0x40400000u, 0x40800000u};
|
||||||
test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::BUFFER_STORE_DWORD,
|
test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::BUFFER_STORE_DWORD,
|
||||||
O::S_ENDPGM};
|
O::S_ENDPGM};
|
||||||
test.sampled_image_rgba = image;
|
test.sampled_image_rgba = image;
|
||||||
test.required_spirv = {"UnpackHalf2x16"};
|
test.required_spirv = {"UnpackHalf2x16", "Lod|ConstOffset"};
|
||||||
test.forbidden_spirv = {"OpBitFieldSExtract"};
|
test.forbidden_spirv = {"OpBitFieldSExtract", "OpImageQuerySizeLod", "OpFDiv"};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <u32 wave_size> TestCase ImageSampleOffsetsReadFourNeighbors() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
TestCase test;
|
||||||
|
test.name = wave_size == 32 ? "ImageSampleOffsetsReadFourNeighbors32"
|
||||||
|
: "ImageSampleOffsetsReadFourNeighbors64";
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
constexpr std::array offsets{0x01u, 0x3fu, 0x3f00u, 0x0100u};
|
||||||
|
constexpr std::array opcodes{0x37u, 0x37u, 0x34u, 0x32u};
|
||||||
|
for (u32 sample = 0; sample < offsets.size(); ++sample) {
|
||||||
|
AppendVMovLiteral(&test.code, 20, offsets[sample]);
|
||||||
|
u32 coordinate = 21;
|
||||||
|
if (opcodes[sample] == 0x32u) {
|
||||||
|
for (float gradient : {0.25f, 0.f, 0.f, 0.25f}) {
|
||||||
|
AppendVMovLiteral(&test.code, coordinate++, std::bit_cast<u32>(gradient));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// All instructions address the center of texel (2, 1) before applying the offset.
|
||||||
|
AppendVMovLiteral(&test.code, coordinate, std::bit_cast<u32>(0.625f));
|
||||||
|
AppendVMovLiteral(&test.code, coordinate + 1, std::bit_cast<u32>(0.375f));
|
||||||
|
if (opcodes[sample] == 0x34u) AppendVMovU32(&test.code, coordinate + 2, 0);
|
||||||
|
test.code.push_back(EncodeMimg0(opcodes[sample], 0x1));
|
||||||
|
test.code.push_back(EncodeMimg1(sample, 20));
|
||||||
|
AppendStoreVgpr(&test.code, sample, sample);
|
||||||
|
}
|
||||||
|
AppendEnd(&test.code);
|
||||||
|
test.sampled_image_rgba = MakeRgbaImage(4, 4);
|
||||||
|
SetRgbaPixel(&test.sampled_image_rgba, 4, 2, 1, std::bit_cast<u32>(16.f), 0, 0, 0);
|
||||||
|
constexpr std::array<std::array<u32, 2>, 4> neighbors{{{3, 1}, {1, 1}, {2, 0}, {2, 2}}};
|
||||||
|
for (u32 sample = 0; sample < neighbors.size(); ++sample) {
|
||||||
|
const auto value = std::bit_cast<u32>(float(sample + 1));
|
||||||
|
SetRgbaPixel(&test.sampled_image_rgba, 4, neighbors[sample][0], neighbors[sample][1],
|
||||||
|
value, 0, 0, 0);
|
||||||
|
test.expected.push_back(value);
|
||||||
|
}
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.required_spirv = {"Lod|ConstOffset", "Grad|ConstOffset"};
|
||||||
|
test.forbidden_spirv = {"OpImageQuerySizeLod", "OpFDiv", "OpBitFieldSExtract", "OpSwitch"};
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35058,7 +35102,8 @@ TestCase ImageGatherCompareOpcodes() {
|
|||||||
O::V_MOV_B32, O::IMAGE_GATHER4_C, O::IMAGE_GATHER4_C_LZ,
|
O::V_MOV_B32, O::IMAGE_GATHER4_C, O::IMAGE_GATHER4_C_LZ,
|
||||||
O::IMAGE_GATHER4_C_O, O::IMAGE_GATHER4_C_LZ_O, O::BUFFER_STORE_DWORD,
|
O::IMAGE_GATHER4_C_O, O::IMAGE_GATHER4_C_LZ_O, O::BUFFER_STORE_DWORD,
|
||||||
O::S_ENDPGM};
|
O::S_ENDPGM};
|
||||||
test.required_spirv = {"OpImageDrefGather", "OpBitFieldSExtract"};
|
test.required_spirv = {"OpImageDrefGather", "ConstOffset"};
|
||||||
|
test.forbidden_spirv = {"OpBitFieldSExtract"};
|
||||||
test.compile_only = true;
|
test.compile_only = true;
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
@@ -35610,7 +35655,7 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
|||||||
std::memcpy(&memory_flag_bits, &memory_flags, sizeof(memory_flags));
|
std::memcpy(&memory_flag_bits, &memory_flags, sizeof(memory_flags));
|
||||||
auto &sample = block->AppendNewInst(
|
auto &sample = block->AppendNewInst(
|
||||||
ValueOpcode::ImageSampleRaw,
|
ValueOpcode::ImageSampleRaw,
|
||||||
{Value(&image), Value(&sampler), Value(&address)}, memory_flag_bits);
|
{Value(&image), Value(&sampler), Value(&address), Value(true)}, memory_flag_bits);
|
||||||
auto &sample_x = block->AppendNewInst(ValueOpcode::CompositeExtractU32x4,
|
auto &sample_x = block->AppendNewInst(ValueOpcode::CompositeExtractU32x4,
|
||||||
{Value(&sample), Value(0u)});
|
{Value(&sample), Value(0u)});
|
||||||
block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(&sample_x)});
|
block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(&sample_x)});
|
||||||
@@ -35836,7 +35881,7 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) {
|
|||||||
auto &read = *block->PrependNewInst(
|
auto &read = *block->PrependNewInst(
|
||||||
position, ValueOpcode::ImageGatherRaw,
|
position, ValueOpcode::ImageGatherRaw,
|
||||||
{Value(&image), Value(&sampler), Value(&address), Value(0u), Value(0u),
|
{Value(&image), Value(&sampler), Value(&address), Value(0u), Value(0u),
|
||||||
Value(0u), Value(0u)}, memory_flag_bits);
|
Value(0u), Value(0u), Value(true)}, memory_flag_bits);
|
||||||
access->ReplaceUsesWith(Value(&read), false);
|
access->ReplaceUsesWith(Value(&read), false);
|
||||||
access = &read;
|
access = &read;
|
||||||
compiled.program.info.images[4].cube = false;
|
compiled.program.info.images[4].cube = false;
|
||||||
@@ -37772,7 +37817,10 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(ImageD16GatherPacksHalfPairs);
|
AddCase(ImageD16GatherPacksHalfPairs);
|
||||||
AddCase(ImageSampleA16SamplerCoordsOnGpu);
|
AddCase(ImageSampleA16SamplerCoordsOnGpu);
|
||||||
AddCase(ImageSampleOpcodeAliasUsesNormalCoords);
|
AddCase(ImageSampleOpcodeAliasUsesNormalCoords);
|
||||||
AddCase(ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu);
|
AddCase(ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<32>);
|
||||||
|
AddCase(ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<64>);
|
||||||
|
AddCase(ImageSampleOffsetsReadFourNeighbors<32>);
|
||||||
|
AddCase(ImageSampleOffsetsReadFourNeighbors<64>);
|
||||||
AddCase(ImageSampleA16CompareBiasRdna2AddressOrder);
|
AddCase(ImageSampleA16CompareBiasRdna2AddressOrder);
|
||||||
AddCase(ImageGatherCompareOpcodes);
|
AddCase(ImageGatherCompareOpcodes);
|
||||||
AddCase(ImageStoreVariants);
|
AddCase(ImageStoreVariants);
|
||||||
@@ -40183,7 +40231,7 @@ void CheckImageSamplerSpecialization() {
|
|||||||
sizeof(signed_memory_flags));
|
sizeof(signed_memory_flags));
|
||||||
mixed_sampler_block->AppendNewInst(
|
mixed_sampler_block->AppendNewInst(
|
||||||
ValueOpcode::ImageSampleRaw,
|
ValueOpcode::ImageSampleRaw,
|
||||||
{Value(&image_handle), Value(&sampler_handle), Value(&image_address)},
|
{Value(&image_handle), Value(&sampler_handle), Value(&image_address), Value(true)},
|
||||||
signed_memory_flag_bits);
|
signed_memory_flag_bits);
|
||||||
|
|
||||||
ShaderRecompiler::IR::DescriptorValue native_image_descriptor{};
|
ShaderRecompiler::IR::DescriptorValue native_image_descriptor{};
|
||||||
@@ -43440,6 +43488,16 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, FlatVirtualAddressRebasesGuestAllocation());
|
RunCase(&vulkan, FlatVirtualAddressRebasesGuestAllocation());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--image-sample-offset-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
RunCase(&vulkan, ImageSampleOffsetsReadFourNeighbors<32>());
|
||||||
|
RunCase(&vulkan, ImageSampleOffsetsReadFourNeighbors<64>());
|
||||||
|
RunCase(&vulkan, ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<32>());
|
||||||
|
RunCase(&vulkan, ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<64>());
|
||||||
|
RunCase(&vulkan, ImageSampleAndGather());
|
||||||
|
RunCase(nullptr, ImageGatherCompareOpcodes());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--image-gather-lod-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--image-gather-lod-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
RunCase(&vulkan, ImageGatherExplicitLod());
|
RunCase(&vulkan, ImageGatherExplicitLod());
|
||||||
|
|||||||
@@ -1316,7 +1316,7 @@ void TestShaderFeatureCollection() {
|
|||||||
{Value(0u), Value(0u), Value(0u)});
|
{Value(0u), Value(0u), Value(0u)});
|
||||||
block->AppendNewInst(ValueOpcode::ImageGatherRaw,
|
block->AppendNewInst(ValueOpcode::ImageGatherRaw,
|
||||||
{Value(0u), Value(0u), Value(0u), Value(0u),
|
{Value(0u), Value(0u), Value(0u), Value(0u),
|
||||||
Value(0u), Value(0u), Value(0u)});
|
Value(0u), Value(0u), Value(0u), Value(true)});
|
||||||
auto &shared = block->AppendNewInst(ValueOpcode::LoadSharedU32,
|
auto &shared = block->AppendNewInst(ValueOpcode::LoadSharedU32,
|
||||||
{Value(0u), Value(true)});
|
{Value(0u), Value(true)});
|
||||||
shared.SetFlags(MemoryFlags{.index = 0});
|
shared.SetFlags(MemoryFlags{.index = 0});
|
||||||
|
|||||||
Reference in New Issue
Block a user