[spirv] Support Load methods that take Status arg. (#905)
This commit is contained in:
Родитель
674aa26115
Коммит
1d4509f35d
|
@ -1611,6 +1611,8 @@ Since Buffers are represented as ``OpTypeImage`` with ``Sampled`` set to 1
|
||||||
operation. The return value of ``OpImageFetch`` is always a four-component
|
operation. The return value of ``OpImageFetch`` is always a four-component
|
||||||
vector; so proper additional instructions are generated to truncate the vector
|
vector; so proper additional instructions are generated to truncate the vector
|
||||||
and return the desired number of elements.
|
and return the desired number of elements.
|
||||||
|
If an output unsigned integer ``status`` argument is present, ``OpImageSparseFetch``
|
||||||
|
is used instead. The resulting SPIR-V ``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``operator[]``
|
``operator[]``
|
||||||
++++++++++++++
|
++++++++++++++
|
||||||
|
@ -1628,7 +1630,8 @@ Since Buffers are represented as ``OpTypeImage`` with dimension of ``Buffer``,
|
||||||
+++++++++++
|
+++++++++++
|
||||||
Since RWBuffers are represented as ``OpTypeImage`` with ``Sampled`` set to 2
|
Since RWBuffers are represented as ``OpTypeImage`` with ``Sampled`` set to 2
|
||||||
(meaning to be used without a sampler), ``OpImageRead`` is used to perform this
|
(meaning to be used without a sampler), ``OpImageRead`` is used to perform this
|
||||||
operation.
|
operation. If an output unsigned integer ``status`` argument is present, ``OpImageSparseRead``
|
||||||
|
is used instead. The resulting SPIR-V ``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``operator[]``
|
``operator[]``
|
||||||
++++++++++++++
|
++++++++++++++
|
||||||
|
@ -1780,8 +1783,8 @@ for that texture type.
|
||||||
Common texture methods
|
Common texture methods
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
``.Sample(sampler, location[, offset])``
|
``.Sample(sampler, location[, offset][, clamp][, Status])``
|
||||||
++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
||||||
|
|
||||||
|
@ -1790,12 +1793,15 @@ since texture types are represented as ``OpTypeImage``. An ``OpSampledImage`` is
|
||||||
created based on the ``sampler`` passed to the function. The resulting sampled
|
created based on the ``sampler`` passed to the function. The resulting sampled
|
||||||
image and the ``location`` passed to the function are used as arguments to
|
image and the ``location`` passed to the function are used as arguments to
|
||||||
``OpImageSampleImplicitLod``, with the optional ``offset`` tranlated into
|
``OpImageSampleImplicitLod``, with the optional ``offset`` tranlated into
|
||||||
addtional SPIR-V image operands ``ConstOffset`` or ``Offset`` on it.
|
addtional SPIR-V image operands ``ConstOffset`` or ``Offset`` on it. The optional
|
||||||
|
``clamp`` argument will be translated to the ``MinLod`` image operand.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
If an output unsigned integer ``status`` argument is present,
|
||||||
|
``OpImageSparseSampleImplicitLod`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``.SampleLevel(sampler, location, lod[, offset])``
|
``.SampleLevel(sampler, location, lod[, offset][, Status])``
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
||||||
|
|
||||||
|
@ -1807,21 +1813,26 @@ is attached to the instruction as an SPIR-V image operands ``Lod``. The optional
|
||||||
``offset`` is also tranlated into addtional SPIR-V image operands ``ConstOffset``
|
``offset`` is also tranlated into addtional SPIR-V image operands ``ConstOffset``
|
||||||
or ``Offset`` on it.
|
or ``Offset`` on it.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
If an output unsigned integer ``status`` argument is present,
|
||||||
|
``OpImageSparseSampleExplicitLod`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``.SampleGrad(sampler, location, ddx, ddy[, offset])``
|
``.SampleGrad(sampler, location, ddx, ddy[, offset][, clamp][, Status])``
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
||||||
|
|
||||||
Similarly to ``.SampleLevel``, the ``ddx`` and ``ddy`` parameter are attached to
|
Similarly to ``.SampleLevel``, the ``ddx`` and ``ddy`` parameter are attached to
|
||||||
the ``OpImageSampleExplicitLod`` instruction as an SPIR-V image operands
|
the ``OpImageSampleExplicitLod`` instruction as an SPIR-V image operands
|
||||||
``Grad``.
|
``Grad``. The optional ``clamp`` argument will be translated into the ``MinLod``
|
||||||
|
image operand.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
If an output unsigned integer ``status`` argument is present,
|
||||||
|
``OpImageSparseSampleExplicitLod`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``.SampleBias(sampler, location, bias[, offset])``
|
``.SampleBias(sampler, location, bias[, offset][, clamp][, Status])``
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
Not available to ``Texture2DMS`` and ``Texture2DMSArray``.
|
||||||
|
|
||||||
|
@ -1829,18 +1840,24 @@ The translation is similar to ``.Sample()``, with the ``bias`` parameter
|
||||||
attached to the ``OpImageSampleImplicitLod`` instruction as an SPIR-V image
|
attached to the ``OpImageSampleImplicitLod`` instruction as an SPIR-V image
|
||||||
operands ``Bias``.
|
operands ``Bias``.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
If an output unsigned integer ``status`` argument is present,
|
||||||
|
``OpImageSparseSampleImplicitLod`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``.SampleCmp(sampler, location, comparator[, offset])``
|
``.SampleCmp(sampler, location, comparator[, offset][, clamp][, Status])``
|
||||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Not available to ``Texture3D``, ``Texture2DMS``, and ``Texture2DMSArray``.
|
Not available to ``Texture3D``, ``Texture2DMS``, and ``Texture2DMSArray``.
|
||||||
|
|
||||||
The translation is similar to ``.Sample()``, but the
|
The translation is similar to ``.Sample()``, but the
|
||||||
``OpImageSampleDrefImplicitLod`` instruction are used.
|
``OpImageSampleDrefImplicitLod`` instruction are used.
|
||||||
|
|
||||||
``.SampleCmpLevelZero(sampler, location, comparator[, offset])``
|
If an output unsigned integer ``status`` argument is present,
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
``OpImageSparseSampleDrefImplicitLod`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
|
``.SampleCmpLevelZero(sampler, location, comparator[, offset][, Status])``
|
||||||
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Not available to ``Texture3D``, ``Texture2DMS``, and ``Texture2DMSArray``.
|
Not available to ``Texture3D``, ``Texture2DMS``, and ``Texture2DMSArray``.
|
||||||
|
|
||||||
|
@ -1848,6 +1865,10 @@ The translation is similar to ``.Sample()``, but the
|
||||||
``OpImageSampleDrefExplicitLod`` instruction are used, with the additional
|
``OpImageSampleDrefExplicitLod`` instruction are used, with the additional
|
||||||
``Lod`` image operands set to 0.0.
|
``Lod`` image operands set to 0.0.
|
||||||
|
|
||||||
|
If an output unsigned integer ``status`` argument is present,
|
||||||
|
``OpImageSparseSampleDrefExplicitLod`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``.Gather()``
|
``.Gather()``
|
||||||
+++++++++++++
|
+++++++++++++
|
||||||
|
|
||||||
|
@ -1857,7 +1878,9 @@ Available to ``Texture2D``, ``Texture2DArray``, ``TextureCube``, and
|
||||||
The translation is similar to ``.Sample()``, but the ``OpImageGather``
|
The translation is similar to ``.Sample()``, but the ``OpImageGather``
|
||||||
instruction is used, with component setting to 0.
|
instruction is used, with component setting to 0.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
If an output unsigned integer ``status`` argument is present,
|
||||||
|
``OpImageSparseGather`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``.GatherRed()``, ``.GatherGreen()``, ``.GatherBlue()``, ``.GatherAlpha()``
|
``.GatherRed()``, ``.GatherGreen()``, ``.GatherBlue()``, ``.GatherAlpha()``
|
||||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
@ -1873,7 +1896,9 @@ There are a few overloads for these functions:
|
||||||
- For those overloads taking 4 offset parameters, those offset parameters will
|
- For those overloads taking 4 offset parameters, those offset parameters will
|
||||||
be conveyed as an additional ``ConstOffsets`` image operands to the
|
be conveyed as an additional ``ConstOffsets`` image operands to the
|
||||||
instruction. So those offset parameters must all be constant values.
|
instruction. So those offset parameters must all be constant values.
|
||||||
- Those overloads with the status parameter are not supported.
|
- For those overloads with the ``status`` parameter, ``OpImageSparseGather``
|
||||||
|
is used instead, and the resulting SPIR-V ``Residency Code`` will be
|
||||||
|
written to ``status``.
|
||||||
|
|
||||||
``.GatherCmp()``
|
``.GatherCmp()``
|
||||||
++++++++++++++++
|
++++++++++++++++
|
||||||
|
@ -1884,7 +1909,10 @@ Available to ``Texture2D``, ``Texture2DArray``, ``TextureCube``, and
|
||||||
The translation is similar to ``.Sample()``, but the ``OpImageDrefGather``
|
The translation is similar to ``.Sample()``, but the ``OpImageDrefGather``
|
||||||
instruction is used.
|
instruction is used.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
For the overload with the output unsigned integer ``status`` argument,
|
||||||
|
``OpImageSparseDrefGather`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
|
|
||||||
``.GatherCmpRed()``
|
``.GatherCmpRed()``
|
||||||
+++++++++++++++++++
|
+++++++++++++++++++
|
||||||
|
@ -1894,8 +1922,6 @@ Available to ``Texture2D``, ``Texture2DArray``, ``TextureCube``, and
|
||||||
|
|
||||||
The translation is the same as ``.GatherCmp()``.
|
The translation is the same as ``.GatherCmp()``.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
|
||||||
|
|
||||||
``.Load(location[, sampleIndex][, offset])``
|
``.Load(location[, sampleIndex][, offset])``
|
||||||
++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
@ -1908,7 +1934,9 @@ The return value of ``OpImageFetch`` is always a four-component vector; so
|
||||||
proper additional instructions are generated to truncate the vector and return
|
proper additional instructions are generated to truncate the vector and return
|
||||||
the desired number of elements.
|
the desired number of elements.
|
||||||
|
|
||||||
The overload with the status parameter are not supported.
|
For the overload with the output unsigned integer ``status`` argument,
|
||||||
|
``OpImageSparseFetch`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``operator[]``
|
``operator[]``
|
||||||
++++++++++++++
|
++++++++++++++
|
||||||
|
@ -2034,6 +2062,10 @@ Since read-write texture types are represented as ``OpTypeImage`` with
|
||||||
``Sampled`` set to 2 (meaning to be used without a sampler), ``OpImageRead`` is
|
``Sampled`` set to 2 (meaning to be used without a sampler), ``OpImageRead`` is
|
||||||
used to perform this operation.
|
used to perform this operation.
|
||||||
|
|
||||||
|
For the overload with the output unsigned integer ``status`` argument,
|
||||||
|
``OpImageSparseRead`` is used instead. The resulting SPIR-V
|
||||||
|
``Residency Code`` will be written to ``status``.
|
||||||
|
|
||||||
``operator[]``
|
``operator[]``
|
||||||
++++++++++++++
|
++++++++++++++
|
||||||
Using ``operator[]`` for reading is handled similarly as ``.Load()``, while for
|
Using ``operator[]`` for reading is handled similarly as ``.Load()``, while for
|
||||||
|
@ -2299,3 +2331,7 @@ either because of no Vulkan equivalents at the moment, or because of deprecation
|
||||||
sample currently being processed.) The compiler will emit an error.
|
sample currently being processed.) The compiler will emit an error.
|
||||||
* ``SV_InnerCoverage`` semantic does not have a Vulkan equivalent. The compiler
|
* ``SV_InnerCoverage`` semantic does not have a Vulkan equivalent. The compiler
|
||||||
will emit an error.
|
will emit an error.
|
||||||
|
* Since ``StructuredBuffer``, ``RWStructuredBuffer``, ``ByteAddressBuffer``, and
|
||||||
|
``RWByteAddressBuffer`` are not represented as image types in SPIR-V, using the
|
||||||
|
output unsigned integer ``status`` argument in their ``Load*`` methods is not
|
||||||
|
supported. Using these methods with the ``status`` argument will cause a compiler error.
|
||||||
|
|
|
@ -865,6 +865,14 @@ public:
|
||||||
llvm::Optional<spv::ImageOperandsMask> image_operands,
|
llvm::Optional<spv::ImageOperandsMask> image_operands,
|
||||||
bool is_explicit, bool is_sparse);
|
bool is_explicit, bool is_sparse);
|
||||||
|
|
||||||
|
// All-in-one method for creating different types of
|
||||||
|
// OpImageRead*/OpImageFetch*.
|
||||||
|
InstBuilder &
|
||||||
|
opImageFetchRead(uint32_t result_type, uint32_t result_id, uint32_t image,
|
||||||
|
uint32_t coordinate,
|
||||||
|
llvm::Optional<spv::ImageOperandsMask> image_operands,
|
||||||
|
bool is_fetch, bool is_sparse);
|
||||||
|
|
||||||
// Methods for supplying additional parameters.
|
// Methods for supplying additional parameters.
|
||||||
InstBuilder &fPFastMathMode(spv::FPFastMathModeMask);
|
InstBuilder &fPFastMathMode(spv::FPFastMathModeMask);
|
||||||
InstBuilder &fPRoundingMode(spv::FPRoundingMode);
|
InstBuilder &fPRoundingMode(spv::FPRoundingMode);
|
||||||
|
|
|
@ -191,11 +191,16 @@ public:
|
||||||
/// doImageFetch is true, OpImageFetch is used. OpImageRead is used otherwise.
|
/// doImageFetch is true, OpImageFetch is used. OpImageRead is used otherwise.
|
||||||
/// OpImageFetch should be used for sampled images. OpImageRead should be used
|
/// OpImageFetch should be used for sampled images. OpImageRead should be used
|
||||||
/// for images without a sampler.
|
/// for images without a sampler.
|
||||||
|
///
|
||||||
|
/// If residencyCodeId is not zero, the sparse version of the instructions
|
||||||
|
/// will be used, and the SPIR-V instruction for storing the resulting
|
||||||
|
/// residency code will also be emitted.
|
||||||
uint32_t createImageFetchOrRead(bool doImageFetch, uint32_t texelType,
|
uint32_t createImageFetchOrRead(bool doImageFetch, uint32_t texelType,
|
||||||
QualType imageType, uint32_t image,
|
QualType imageType, uint32_t image,
|
||||||
uint32_t coordinate, uint32_t lod,
|
uint32_t coordinate, uint32_t lod,
|
||||||
uint32_t constOffset, uint32_t varOffset,
|
uint32_t constOffset, uint32_t varOffset,
|
||||||
uint32_t constOffsets, uint32_t sample);
|
uint32_t constOffsets, uint32_t sample,
|
||||||
|
uint32_t residencyCodeId);
|
||||||
|
|
||||||
/// \brief Creates SPIR-V instructions for writing to the given image.
|
/// \brief Creates SPIR-V instructions for writing to the given image.
|
||||||
void createImageWrite(QualType imageType, uint32_t imageId, uint32_t coordId,
|
void createImageWrite(QualType imageType, uint32_t imageId, uint32_t coordId,
|
||||||
|
|
|
@ -112,6 +112,28 @@ InstBuilder &InstBuilder::opImageSample(
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstBuilder &InstBuilder::opImageFetchRead(
|
||||||
|
uint32_t result_type, uint32_t result_id, uint32_t image,
|
||||||
|
uint32_t coordinate, llvm::Optional<spv::ImageOperandsMask> image_operands,
|
||||||
|
bool is_fetch, bool is_sparse) {
|
||||||
|
spv::Op op =
|
||||||
|
is_fetch
|
||||||
|
? (is_sparse ? spv::Op::OpImageSparseFetch : spv::Op::OpImageFetch)
|
||||||
|
: (is_sparse ? spv::Op::OpImageSparseRead : spv::Op::OpImageRead);
|
||||||
|
|
||||||
|
TheInst.emplace_back(static_cast<uint32_t>(op));
|
||||||
|
TheInst.emplace_back(result_type);
|
||||||
|
TheInst.emplace_back(result_id);
|
||||||
|
TheInst.emplace_back(image);
|
||||||
|
TheInst.emplace_back(coordinate);
|
||||||
|
if (image_operands.hasValue()) {
|
||||||
|
const auto &val = image_operands.getValue();
|
||||||
|
encodeImageOperands(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void InstBuilder::encodeString(std::string value) {
|
void InstBuilder::encodeString(std::string value) {
|
||||||
const auto &words = string::encodeSPIRVString(value);
|
const auto &words = string::encodeSPIRVString(value);
|
||||||
TheInst.insert(TheInst.end(), words.begin(), words.end());
|
TheInst.insert(TheInst.end(), words.begin(), words.end());
|
||||||
|
|
|
@ -429,30 +429,44 @@ void ModuleBuilder::createImageWrite(QualType imageType, uint32_t imageId,
|
||||||
uint32_t ModuleBuilder::createImageFetchOrRead(
|
uint32_t ModuleBuilder::createImageFetchOrRead(
|
||||||
bool doImageFetch, uint32_t texelType, QualType imageType, uint32_t image,
|
bool doImageFetch, uint32_t texelType, QualType imageType, uint32_t image,
|
||||||
uint32_t coordinate, uint32_t lod, uint32_t constOffset, uint32_t varOffset,
|
uint32_t coordinate, uint32_t lod, uint32_t constOffset, uint32_t varOffset,
|
||||||
uint32_t constOffsets, uint32_t sample) {
|
uint32_t constOffsets, uint32_t sample, uint32_t residencyCodeId) {
|
||||||
assert(insertPoint && "null insert point");
|
assert(insertPoint && "null insert point");
|
||||||
|
|
||||||
// TODO: Update ImageFetch/ImageRead to accept minLod if necessary.
|
|
||||||
llvm::SmallVector<uint32_t, 2> params;
|
llvm::SmallVector<uint32_t, 2> params;
|
||||||
const auto mask =
|
const auto mask =
|
||||||
llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
|
llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
|
||||||
/*bias*/ 0, lod, std::make_pair(0, 0), constOffset, varOffset,
|
/*bias*/ 0, lod, std::make_pair(0, 0), constOffset, varOffset,
|
||||||
constOffsets, sample, /*minLod*/ 0, ¶ms));
|
constOffsets, sample, /*minLod*/ 0, ¶ms));
|
||||||
|
|
||||||
const uint32_t texelId = theContext.takeNextId();
|
const bool isSparse = (residencyCodeId != 0);
|
||||||
if (doImageFetch) {
|
uint32_t retType = texelType;
|
||||||
instBuilder.opImageFetch(texelType, texelId, image, coordinate, mask);
|
if (isSparse) {
|
||||||
} else {
|
requireCapability(spv::Capability::SparseResidency);
|
||||||
|
retType = getSparseResidencyStructType(texelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doImageFetch) {
|
||||||
requireCapability(
|
requireCapability(
|
||||||
TypeTranslator::getCapabilityForStorageImageReadWrite(imageType));
|
TypeTranslator::getCapabilityForStorageImageReadWrite(imageType));
|
||||||
instBuilder.opImageRead(texelType, texelId, image, coordinate, mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t texelId = theContext.takeNextId();
|
||||||
|
instBuilder.opImageFetchRead(retType, texelId, image, coordinate, mask,
|
||||||
|
doImageFetch, isSparse);
|
||||||
|
|
||||||
for (const auto param : params)
|
for (const auto param : params)
|
||||||
instBuilder.idRef(param);
|
instBuilder.idRef(param);
|
||||||
instBuilder.x();
|
instBuilder.x();
|
||||||
insertPoint->appendInstruction(std::move(constructSite));
|
insertPoint->appendInstruction(std::move(constructSite));
|
||||||
|
|
||||||
|
if (isSparse) {
|
||||||
|
// Write the Residency Code
|
||||||
|
const auto status = createCompositeExtract(getUint32Type(), texelId, {0});
|
||||||
|
createStore(residencyCodeId, status);
|
||||||
|
// Extract the real result from the struct
|
||||||
|
texelId = createCompositeExtract(texelType, texelId, {1});
|
||||||
|
}
|
||||||
|
|
||||||
return texelId;
|
return texelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2350,11 +2350,9 @@ uint32_t SPIRVEmitter::processTextureGatherCmp(const CXXMemberCallExpr *expr) {
|
||||||
/*sampleNumber*/ 0, status);
|
/*sampleNumber*/ 0, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(const Expr *object,
|
SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
|
||||||
const uint32_t locationId,
|
const Expr *object, const uint32_t locationId, uint32_t constOffset,
|
||||||
uint32_t constOffset,
|
uint32_t varOffset, uint32_t lod, uint32_t residencyCode) {
|
||||||
uint32_t varOffset,
|
|
||||||
uint32_t lod) {
|
|
||||||
// Loading for Buffer and RWBuffer translates to an OpImageFetch.
|
// Loading for Buffer and RWBuffer translates to an OpImageFetch.
|
||||||
// The result type of an OpImageFetch must be a vec4 of float or int.
|
// The result type of an OpImageFetch must be a vec4 of float or int.
|
||||||
const auto type = object->getType();
|
const auto type = object->getType();
|
||||||
|
@ -2393,10 +2391,9 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(const Expr *object,
|
||||||
|
|
||||||
// OpImageFetch and OpImageRead can only fetch a vector of 4 elements.
|
// OpImageFetch and OpImageRead can only fetch a vector of 4 elements.
|
||||||
const uint32_t texelTypeId = theBuilder.getVecType(elemTypeId, 4u);
|
const uint32_t texelTypeId = theBuilder.getVecType(elemTypeId, 4u);
|
||||||
const uint32_t texel =
|
const uint32_t texel = theBuilder.createImageFetchOrRead(
|
||||||
theBuilder.createImageFetchOrRead(doFetch, texelTypeId, type, objectId,
|
doFetch, texelTypeId, type, objectId, locationId, lod, constOffset,
|
||||||
locationId, lod, constOffset, varOffset,
|
varOffset, /*constOffsets*/ 0, sampleNumber, residencyCode);
|
||||||
/*constOffsets*/ 0, sampleNumber);
|
|
||||||
|
|
||||||
uint32_t retVal = texel;
|
uint32_t retVal = texel;
|
||||||
// If the result type is a vec1, vec2, or vec3, some extra processing
|
// If the result type is a vec1, vec2, or vec3, some extra processing
|
||||||
|
@ -2436,7 +2433,7 @@ SpirvEvalInfo SPIRVEmitter::processByteAddressBufferLoadStore(
|
||||||
typeTranslator.isByteAddressBuffer(type));
|
typeTranslator.isByteAddressBuffer(type));
|
||||||
if (expr->getNumArgs() == 2) {
|
if (expr->getNumArgs() == 2) {
|
||||||
emitError(
|
emitError(
|
||||||
"(RW)ByteAddressBuffer::Load(in address, out status) unimplemented",
|
"(RW)ByteAddressBuffer::Load(in address, out status) not supported",
|
||||||
expr->getExprLoc());
|
expr->getExprLoc());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2512,8 +2509,9 @@ SpirvEvalInfo SPIRVEmitter::processByteAddressBufferLoadStore(
|
||||||
SpirvEvalInfo
|
SpirvEvalInfo
|
||||||
SPIRVEmitter::processStructuredBufferLoad(const CXXMemberCallExpr *expr) {
|
SPIRVEmitter::processStructuredBufferLoad(const CXXMemberCallExpr *expr) {
|
||||||
if (expr->getNumArgs() == 2) {
|
if (expr->getNumArgs() == 2) {
|
||||||
emitError("(RW)StructuredBuffer::Load(int, int) unimplemented",
|
emitError(
|
||||||
expr->getExprLoc());
|
"(RW)StructuredBuffer::Load(in location, out status) not supported",
|
||||||
|
expr->getExprLoc());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2644,19 +2642,6 @@ void SPIRVEmitter::handleOffsetInMethodCall(const CXXMemberCallExpr *expr,
|
||||||
*varOffset = doExpr(expr->getArg(index));
|
*varOffset = doExpr(expr->getArg(index));
|
||||||
};
|
};
|
||||||
|
|
||||||
void SPIRVEmitter::handleOptionalOffsetInMethodCall(
|
|
||||||
const CXXMemberCallExpr *expr, uint32_t index, uint32_t *constOffset,
|
|
||||||
uint32_t *varOffset) {
|
|
||||||
*constOffset = *varOffset = 0; // Initialize both first
|
|
||||||
|
|
||||||
if (expr->getNumArgs() == index + 1) { // Has offset argument
|
|
||||||
if (*constOffset = tryToEvaluateAsConst(expr->getArg(index)))
|
|
||||||
return; // Constant offset
|
|
||||||
else
|
|
||||||
*varOffset = doExpr(expr->getArg(index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SpirvEvalInfo
|
SpirvEvalInfo
|
||||||
SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
|
SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
|
||||||
hlsl::IntrinsicOp opcode) {
|
hlsl::IntrinsicOp opcode) {
|
||||||
|
@ -3074,12 +3059,38 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
|
||||||
SpirvEvalInfo
|
SpirvEvalInfo
|
||||||
SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
|
SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
|
||||||
// Signature:
|
// Signature:
|
||||||
|
// For Texture1D, Texture1DArray, Texture2D, Texture2DArray, Texture3D:
|
||||||
|
// ret Object.Load(int Location
|
||||||
|
// [, int Offset]
|
||||||
|
// [, uint status]);
|
||||||
|
//
|
||||||
|
// For Texture2DMS and Texture2DMSArray, there is one additional argument:
|
||||||
// ret Object.Load(int Location
|
// ret Object.Load(int Location
|
||||||
// [, int SampleIndex]
|
// [, int SampleIndex]
|
||||||
// [, int Offset]);
|
// [, int Offset]
|
||||||
|
// [, uint status]);
|
||||||
|
//
|
||||||
|
// For (RW)Buffer, RWTexture1D, RWTexture1DArray, RWTexture2D,
|
||||||
|
// RWTexture2DArray, RWTexture3D:
|
||||||
|
// ret Object.Load (int Location
|
||||||
|
// [, uint status]);
|
||||||
|
//
|
||||||
|
// Note: (RW)ByteAddressBuffer and (RW)StructuredBuffer types also have Load
|
||||||
|
// methods that take an additional Status argument. However, since these types
|
||||||
|
// are not represented as OpTypeImage in SPIR-V, we don't have a way of
|
||||||
|
// figuring out the Residency Code for them. Therefore having the Status
|
||||||
|
// argument for these types is not supported.
|
||||||
|
//
|
||||||
|
// For (RW)ByteAddressBuffer:
|
||||||
|
// ret Object.{Load,Load2,Load3,Load4} (int Location
|
||||||
|
// [, uint status]);
|
||||||
|
//
|
||||||
|
// For (RW)StructuredBuffer:
|
||||||
|
// ret Object.Load (int Location
|
||||||
|
// [, uint status]);
|
||||||
|
//
|
||||||
|
|
||||||
const auto *object = expr->getImplicitObjectArgument();
|
const auto *object = expr->getImplicitObjectArgument();
|
||||||
const auto *location = expr->getArg(0);
|
|
||||||
const auto objectType = object->getType();
|
const auto objectType = object->getType();
|
||||||
|
|
||||||
if (typeTranslator.isRWByteAddressBuffer(objectType) ||
|
if (typeTranslator.isRWByteAddressBuffer(objectType) ||
|
||||||
|
@ -3089,10 +3100,23 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
|
||||||
if (TypeTranslator::isStructuredBuffer(objectType))
|
if (TypeTranslator::isStructuredBuffer(objectType))
|
||||||
return processStructuredBufferLoad(expr);
|
return processStructuredBufferLoad(expr);
|
||||||
|
|
||||||
|
const auto numArgs = expr->getNumArgs();
|
||||||
|
const auto *location = expr->getArg(0);
|
||||||
|
const bool isTextureMS = TypeTranslator::isTextureMS(objectType);
|
||||||
|
const bool hasStatusArg =
|
||||||
|
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
|
||||||
|
const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
|
||||||
|
|
||||||
if (TypeTranslator::isBuffer(objectType) ||
|
if (TypeTranslator::isBuffer(objectType) ||
|
||||||
TypeTranslator::isRWBuffer(objectType) ||
|
TypeTranslator::isRWBuffer(objectType) ||
|
||||||
TypeTranslator::isRWTexture(objectType))
|
TypeTranslator::isRWTexture(objectType))
|
||||||
return processBufferTextureLoad(object, doExpr(location));
|
return processBufferTextureLoad(object, doExpr(location), /*constOffset*/ 0,
|
||||||
|
/*varOffset*/ 0, /*lod*/ 0,
|
||||||
|
/*residencyCode*/ status);
|
||||||
|
|
||||||
|
// Subtract 1 for status (if it exists), and 1 for sampleIndex (if it exists),
|
||||||
|
// and 1 for location.
|
||||||
|
const bool hasOffsetArg = numArgs - hasStatusArg - isTextureMS - 1 > 0;
|
||||||
|
|
||||||
if (TypeTranslator::isTexture(objectType)) {
|
if (TypeTranslator::isTexture(objectType)) {
|
||||||
// .Load() has a second optional paramter for offset.
|
// .Load() has a second optional paramter for offset.
|
||||||
|
@ -3100,12 +3124,13 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
|
||||||
uint32_t constOffset = 0, varOffset = 0;
|
uint32_t constOffset = 0, varOffset = 0;
|
||||||
uint32_t coordinate = locationId, lod = 0;
|
uint32_t coordinate = locationId, lod = 0;
|
||||||
|
|
||||||
if (TypeTranslator::isTextureMS(objectType)) {
|
if (isTextureMS) {
|
||||||
// SampleIndex is only available when the Object is of Texture2DMS or
|
// SampleIndex is only available when the Object is of Texture2DMS or
|
||||||
// Texture2DMSArray types. Under those cases, Offset will be the third
|
// Texture2DMSArray types. Under those cases, Offset will be the third
|
||||||
// parameter (index 2).
|
// parameter (index 2).
|
||||||
lod = doExpr(expr->getArg(1));
|
lod = doExpr(expr->getArg(1));
|
||||||
handleOptionalOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
|
if(hasOffsetArg)
|
||||||
|
handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
|
||||||
} else {
|
} else {
|
||||||
// For Texture Load() functions, the location parameter is a vector
|
// For Texture Load() functions, the location parameter is a vector
|
||||||
// that consists of both the coordinate and the mipmap level (via the
|
// that consists of both the coordinate and the mipmap level (via the
|
||||||
|
@ -3114,11 +3139,12 @@ SPIRVEmitter::processBufferTextureLoad(const CXXMemberCallExpr *expr) {
|
||||||
splitVecLastElement(location->getType(), locationId, &coordinate, &lod);
|
splitVecLastElement(location->getType(), locationId, &coordinate, &lod);
|
||||||
// For textures other than Texture2DMS(Array), offset should be the
|
// For textures other than Texture2DMS(Array), offset should be the
|
||||||
// second parameter (index 1).
|
// second parameter (index 1).
|
||||||
handleOptionalOffsetInMethodCall(expr, 1, &constOffset, &varOffset);
|
if(hasOffsetArg)
|
||||||
|
handleOffsetInMethodCall(expr, 1, &constOffset, &varOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return processBufferTextureLoad(object, coordinate, constOffset, varOffset,
|
return processBufferTextureLoad(object, coordinate, constOffset, varOffset,
|
||||||
lod);
|
lod, status);
|
||||||
}
|
}
|
||||||
emitError("Load() of the given object type unimplemented",
|
emitError("Load() of the given object type unimplemented",
|
||||||
object->getExprLoc());
|
object->getExprLoc());
|
||||||
|
@ -3158,13 +3184,15 @@ SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
|
||||||
? theBuilder.getConstantUint32(0)
|
? theBuilder.getConstantUint32(0)
|
||||||
: 0;
|
: 0;
|
||||||
return processBufferTextureLoad(baseExpr, doExpr(indexExpr),
|
return processBufferTextureLoad(baseExpr, doExpr(indexExpr),
|
||||||
/*constOffset*/ 0, /*varOffset*/ 0, lod);
|
/*constOffset*/ 0, /*varOffset*/ 0, lod,
|
||||||
|
/*residencyCode*/ 0);
|
||||||
}
|
}
|
||||||
// .mips[][] or .sample[][] must use the correct slice.
|
// .mips[][] or .sample[][] must use the correct slice.
|
||||||
if (isTextureMipsSampleIndexing(expr, &baseExpr, &indexExpr, &lodExpr)) {
|
if (isTextureMipsSampleIndexing(expr, &baseExpr, &indexExpr, &lodExpr)) {
|
||||||
const uint32_t lod = doExpr(lodExpr);
|
const uint32_t lod = doExpr(lodExpr);
|
||||||
return processBufferTextureLoad(baseExpr, doExpr(indexExpr),
|
return processBufferTextureLoad(baseExpr, doExpr(indexExpr),
|
||||||
/*constOffset*/ 0, /*varOffset*/ 0, lod);
|
/*constOffset*/ 0, /*varOffset*/ 0, lod,
|
||||||
|
/*residencyCode*/ 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -576,14 +576,6 @@ private:
|
||||||
void processSwitchStmtUsingIfStmts(const SwitchStmt *switchStmt);
|
void processSwitchStmtUsingIfStmts(const SwitchStmt *switchStmt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Handles the optional offset argument in the given method call at the given
|
|
||||||
/// argument index.
|
|
||||||
/// If there exists an offset argument, writes the <result-id> to either
|
|
||||||
/// *constOffset or *varOffset, depending on the constantness of the offset.
|
|
||||||
void handleOptionalOffsetInMethodCall(const CXXMemberCallExpr *expr,
|
|
||||||
uint32_t index, uint32_t *constOffset,
|
|
||||||
uint32_t *varOffset);
|
|
||||||
|
|
||||||
/// Handles the offset argument in the given method call at the given argument
|
/// Handles the offset argument in the given method call at the given argument
|
||||||
/// index. Panics if the argument at the given index does not exist. Writes
|
/// index. Panics if the argument at the given index does not exist. Writes
|
||||||
/// the <result-id> to either *constOffset or *varOffset, depending on the
|
/// the <result-id> to either *constOffset or *varOffset, depending on the
|
||||||
|
@ -598,10 +590,12 @@ private:
|
||||||
/// \brief Loads one element from the given Buffer/RWBuffer/Texture object at
|
/// \brief Loads one element from the given Buffer/RWBuffer/Texture object at
|
||||||
/// the given location. The type of the loaded element matches the type in the
|
/// the given location. The type of the loaded element matches the type in the
|
||||||
/// declaration for the Buffer/Texture object.
|
/// declaration for the Buffer/Texture object.
|
||||||
|
/// If residencyCodeId is not zero, the SPIR-V instruction for storing the
|
||||||
|
/// resulting residency code will also be emitted.
|
||||||
SpirvEvalInfo processBufferTextureLoad(const Expr *object, uint32_t location,
|
SpirvEvalInfo processBufferTextureLoad(const Expr *object, uint32_t location,
|
||||||
uint32_t constOffset = 0,
|
uint32_t constOffset,
|
||||||
uint32_t varOffst = 0,
|
uint32_t varOffset, uint32_t lod,
|
||||||
uint32_t lod = 0);
|
uint32_t residencyCode);
|
||||||
|
|
||||||
/// \brief Processes .Sample() and .Gather() method calls for texture objects.
|
/// \brief Processes .Sample() and .Gather() method calls for texture objects.
|
||||||
uint32_t processTextureSampleGather(const CXXMemberCallExpr *expr,
|
uint32_t processTextureSampleGather(const CXXMemberCallExpr *expr,
|
||||||
|
|
|
@ -15,66 +15,93 @@ RWBuffer<int4> int4buf;
|
||||||
RWBuffer<uint4> uint4buf;
|
RWBuffer<uint4> uint4buf;
|
||||||
RWBuffer<float4> float4buf;
|
RWBuffer<float4> float4buf;
|
||||||
|
|
||||||
|
// CHECK: OpCapability SparseResidency
|
||||||
|
|
||||||
|
// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
int address;
|
int address;
|
||||||
|
|
||||||
// CHECK: [[img1:%\d+]] = OpLoad %type_buffer_image %intbuf
|
// CHECK: [[img1:%\d+]] = OpLoad %type_buffer_image %intbuf
|
||||||
// CHECK: [[f1:%\d+]] = OpImageFetch %v4int [[img1]] {{%\d+}} None
|
// CHECK: [[f1:%\d+]] = OpImageFetch %v4int [[img1]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpCompositeExtract %int [[f1]] 0
|
// CHECK-NEXT: {{%\d+}} = OpCompositeExtract %int [[f1]] 0
|
||||||
int int1 = intbuf.Load(address);
|
int i1 = intbuf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img2:%\d+]] = OpLoad %type_buffer_image_0 %uintbuf
|
// CHECK: [[img2:%\d+]] = OpLoad %type_buffer_image_0 %uintbuf
|
||||||
// CHECK: [[f2:%\d+]] = OpImageFetch %v4uint [[img2]] {{%\d+}} None
|
// CHECK: [[f2:%\d+]] = OpImageFetch %v4uint [[img2]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpCompositeExtract %uint [[f2]] 0
|
// CHECK-NEXT: {{%\d+}} = OpCompositeExtract %uint [[f2]] 0
|
||||||
uint uint1 = uintbuf.Load(address);
|
uint u1 = uintbuf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img3:%\d+]] = OpLoad %type_buffer_image_1 %floatbuf
|
// CHECK: [[img3:%\d+]] = OpLoad %type_buffer_image_1 %floatbuf
|
||||||
// CHECK: [[f3:%\d+]] = OpImageFetch %v4float [[img3]] {{%\d+}} None
|
// CHECK: [[f3:%\d+]] = OpImageFetch %v4float [[img3]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpCompositeExtract %float [[f3]] 0
|
// CHECK-NEXT: {{%\d+}} = OpCompositeExtract %float [[f3]] 0
|
||||||
float float1 = floatbuf.Load(address);
|
float f1 = floatbuf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img4:%\d+]] = OpLoad %type_buffer_image_2 %int2buf
|
// CHECK: [[img4:%\d+]] = OpLoad %type_buffer_image_2 %int2buf
|
||||||
// CHECK: [[f4:%\d+]] = OpImageRead %v4int [[img4]] {{%\d+}} None
|
// CHECK: [[f4:%\d+]] = OpImageRead %v4int [[img4]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2int [[f4]] [[f4]] 0 1
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2int [[f4]] [[f4]] 0 1
|
||||||
int2 int2 = int2buf.Load(address);
|
int2 i2 = int2buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img5:%\d+]] = OpLoad %type_buffer_image_3 %uint2buf
|
// CHECK: [[img5:%\d+]] = OpLoad %type_buffer_image_3 %uint2buf
|
||||||
// CHECK: [[f5:%\d+]] = OpImageRead %v4uint [[img5]] {{%\d+}} None
|
// CHECK: [[f5:%\d+]] = OpImageRead %v4uint [[img5]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2uint [[f5]] [[f5]] 0 1
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2uint [[f5]] [[f5]] 0 1
|
||||||
uint2 uint2 = uint2buf.Load(address);
|
uint2 u2 = uint2buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img6:%\d+]] = OpLoad %type_buffer_image_4 %float2buf
|
// CHECK: [[img6:%\d+]] = OpLoad %type_buffer_image_4 %float2buf
|
||||||
// CHECK: [[f6:%\d+]] = OpImageRead %v4float [[img6]] {{%\d+}} None
|
// CHECK: [[f6:%\d+]] = OpImageRead %v4float [[img6]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2float [[f6]] [[f6]] 0 1
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v2float [[f6]] [[f6]] 0 1
|
||||||
float2 float2 = float2buf.Load(address);
|
float2 f2 = float2buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img7:%\d+]] = OpLoad %type_buffer_image_5 %int3buf
|
// CHECK: [[img7:%\d+]] = OpLoad %type_buffer_image_5 %int3buf
|
||||||
// CHECK: [[f7:%\d+]] = OpImageFetch %v4int [[img7]] {{%\d+}} None
|
// CHECK: [[f7:%\d+]] = OpImageFetch %v4int [[img7]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3int [[f7]] [[f7]] 0 1 2
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3int [[f7]] [[f7]] 0 1 2
|
||||||
int3 int3 = int3buf.Load(address);
|
int3 i3 = int3buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img8:%\d+]] = OpLoad %type_buffer_image_6 %uint3buf
|
// CHECK: [[img8:%\d+]] = OpLoad %type_buffer_image_6 %uint3buf
|
||||||
// CHECK: [[f8:%\d+]] = OpImageFetch %v4uint [[img8]] {{%\d+}} None
|
// CHECK: [[f8:%\d+]] = OpImageFetch %v4uint [[img8]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3uint [[f8]] [[f8]] 0 1 2
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3uint [[f8]] [[f8]] 0 1 2
|
||||||
uint3 uint3 = uint3buf.Load(address);
|
uint3 u3 = uint3buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img9:%\d+]] = OpLoad %type_buffer_image_7 %float3buf
|
// CHECK: [[img9:%\d+]] = OpLoad %type_buffer_image_7 %float3buf
|
||||||
// CHECK: [[f9:%\d+]] = OpImageFetch %v4float [[img9]] {{%\d+}} None
|
// CHECK: [[f9:%\d+]] = OpImageFetch %v4float [[img9]] {{%\d+}} None
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3float [[f9]] [[f9]] 0 1 2
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3float [[f9]] [[f9]] 0 1 2
|
||||||
float3 float3 = float3buf.Load(address);
|
float3 f3 = float3buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img10:%\d+]] = OpLoad %type_buffer_image_8 %int4buf
|
// CHECK: [[img10:%\d+]] = OpLoad %type_buffer_image_8 %int4buf
|
||||||
// CHECK: {{%\d+}} = OpImageRead %v4int [[img10]] {{%\d+}} None
|
// CHECK: {{%\d+}} = OpImageRead %v4int [[img10]] {{%\d+}} None
|
||||||
// CHECK-NEXT: OpStore %int4 {{%\d+}}
|
// CHECK-NEXT: OpStore %i4 {{%\d+}}
|
||||||
int4 int4 = int4buf.Load(address);
|
int4 i4 = int4buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img11:%\d+]] = OpLoad %type_buffer_image_9 %uint4buf
|
// CHECK: [[img11:%\d+]] = OpLoad %type_buffer_image_9 %uint4buf
|
||||||
// CHECK: {{%\d+}} = OpImageRead %v4uint [[img11]] {{%\d+}} None
|
// CHECK: {{%\d+}} = OpImageRead %v4uint [[img11]] {{%\d+}} None
|
||||||
// CHECK-NEXT: OpStore %uint4 {{%\d+}}
|
// CHECK-NEXT: OpStore %u4 {{%\d+}}
|
||||||
uint4 uint4 = uint4buf.Load(address);
|
uint4 u4 = uint4buf.Load(address);
|
||||||
|
|
||||||
// CHECK: [[img12:%\d+]] = OpLoad %type_buffer_image_10 %float4buf
|
// CHECK: [[img12:%\d+]] = OpLoad %type_buffer_image_10 %float4buf
|
||||||
// CHECK: {{%\d+}} = OpImageRead %v4float [[img12]] {{%\d+}} None
|
// CHECK: {{%\d+}} = OpImageRead %v4float [[img12]] {{%\d+}} None
|
||||||
// CHECK-NEXT: OpStore %float4 {{%\d+}}
|
// CHECK-NEXT: OpStore %f4 {{%\d+}}
|
||||||
float4 float4 = float4buf.Load(address);
|
float4 f4 = float4buf.Load(address);
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// Using the Status argument //
|
||||||
|
///////////////////////////////
|
||||||
|
uint status;
|
||||||
|
|
||||||
|
// CHECK: [[img3:%\d+]] = OpLoad %type_buffer_image_1 %floatbuf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[img3]] {{%\d+}} None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpCompositeExtract %float [[v4result]] 0
|
||||||
|
// CHECK-NEXT: OpStore %r1 [[result]]
|
||||||
|
float r1 = floatbuf.Load(address, status); // Test for Buffer
|
||||||
|
|
||||||
|
// CHECK: [[img6:%\d+]] = OpLoad %type_buffer_image_4 %float2buf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct [[img6]] {{%\d+}} None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v2float [[v4result]] [[v4result]] 0 1
|
||||||
|
// CHECK-NEXT: OpStore %r2 [[result]]
|
||||||
|
float2 r2 = float2buf.Load(address, status); // Test for RWBuffer
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,12 @@ RWTexture3D<float3> float3buf;
|
||||||
RWTexture1DArray<float4> float4buf;
|
RWTexture1DArray<float4> float4buf;
|
||||||
RWTexture2DArray<int3> int3buf;
|
RWTexture2DArray<int3> int3buf;
|
||||||
|
|
||||||
|
// CHECK: OpCapability SparseResidency
|
||||||
|
|
||||||
|
// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4int
|
||||||
|
// CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4uint
|
||||||
|
// CHECK: %SparseResidencyStruct_1 = OpTypeStruct %uint %v4float
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
||||||
// CHECK: [[img1:%\d+]] = OpLoad %type_1d_image %intbuf
|
// CHECK: [[img1:%\d+]] = OpLoad %type_1d_image %intbuf
|
||||||
|
@ -36,4 +42,49 @@ void main() {
|
||||||
// CHECK-NEXT: [[r5:%\d+]] = OpVectorShuffle %v3int [[ret5]] [[ret5]] 0 1 2
|
// CHECK-NEXT: [[r5:%\d+]] = OpVectorShuffle %v3int [[ret5]] [[ret5]] 0 1 2
|
||||||
// CHECK-NEXT: OpStore %e [[r5]]
|
// CHECK-NEXT: OpStore %e [[r5]]
|
||||||
int3 e = int3buf.Load(0);
|
int3 e = int3buf.Load(0);
|
||||||
|
|
||||||
|
uint status;
|
||||||
|
// CHECK: [[img1:%\d+]] = OpLoad %type_1d_image %intbuf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct [[img1]] %int_0 None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4int [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpCompositeExtract %int [[v4result]] 0
|
||||||
|
// CHECK-NEXT: OpStore %a2 [[result]]
|
||||||
|
int a2 = intbuf.Load(0, status);
|
||||||
|
|
||||||
|
// CHECK: [[img2:%\d+]] = OpLoad %type_2d_image %uint2buf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct_0 [[img2]] {{%\d+}} None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4uint [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v2uint [[v4result]] [[v4result]] 0 1
|
||||||
|
// CHECK-NEXT: OpStore %b2 [[result]]
|
||||||
|
uint2 b2 = uint2buf.Load(0, status);
|
||||||
|
|
||||||
|
// CHECK: [[img3:%\d+]] = OpLoad %type_3d_image %float3buf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct_1 [[img3]] {{%\d+}} None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
|
||||||
|
// CHECK-NEXT: OpStore %c2 [[result]]
|
||||||
|
float3 c2 = float3buf.Load(0, status);
|
||||||
|
|
||||||
|
// CHECK: [[img4:%\d+]] = OpLoad %type_1d_image_array %float4buf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct_1 [[img4]] {{%\d+}} None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: OpStore %d2 [[v4result]]
|
||||||
|
float4 d2 = float4buf.Load(0, status);
|
||||||
|
|
||||||
|
// CHECK: [[img5:%\d+]] = OpLoad %type_2d_image_array %int3buf
|
||||||
|
// CHECK-NEXT: [[structResult:%\d+]] = OpImageSparseRead %SparseResidencyStruct [[img5]] {{%\d+}} None
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4int [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v3int [[v4result]] [[v4result]] 0 1 2
|
||||||
|
// CHECK-NEXT: OpStore %e2 [[result]]
|
||||||
|
int3 e2 = int3buf.Load(0, status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,12 @@ Texture1DArray <float4> t1 : register(t1);
|
||||||
Texture2DArray <float4> t2 : register(t2);
|
Texture2DArray <float4> t2 : register(t2);
|
||||||
// .Load() does not support TextureCubeArray.
|
// .Load() does not support TextureCubeArray.
|
||||||
|
|
||||||
|
// CHECK: OpCapability SparseResidency
|
||||||
|
|
||||||
|
// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float
|
||||||
|
|
||||||
// CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_1 %int_2 %int_3
|
// CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_1 %int_2 %int_3
|
||||||
|
// CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
|
||||||
|
|
||||||
float4 main(int4 location: A) : SV_Target {
|
float4 main(int4 location: A) : SV_Target {
|
||||||
|
|
||||||
|
@ -21,9 +26,27 @@ float4 main(int4 location: A) : SV_Target {
|
||||||
// CHECK-NEXT: {{%\d+}} = OpImageFetch %v4float [[t2]] [[coord]] Lod [[lod]]
|
// CHECK-NEXT: {{%\d+}} = OpImageFetch %v4float [[t2]] [[coord]] Lod [[lod]]
|
||||||
float4 val2 = t2.Load(location);
|
float4 val2 = t2.Load(location);
|
||||||
|
|
||||||
|
uint status;
|
||||||
|
// CHECK: [[coord:%\d+]] = OpVectorShuffle %v2int [[v3ic]] [[v3ic]] 0 1
|
||||||
|
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[v3ic]] 2
|
||||||
|
// CHECK-NEXT: [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t1]] [[coord]] Lod|ConstOffset [[lod]] %int_10
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: OpStore %val3 [[result]]
|
||||||
|
float4 val3 = t1.Load(int3(1, 2, 3), 10, status);
|
||||||
|
|
||||||
|
// CHECK: [[loc:%\d+]] = OpLoad %v4int %location
|
||||||
|
// CHECK-NEXT: [[coord:%\d+]] = OpVectorShuffle %v3int [[loc]] [[loc]] 0 1 2
|
||||||
|
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[loc]] 3
|
||||||
|
// CHECK-NEXT: [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t2]] [[coord]] Lod|ConstOffset [[lod]] [[v2ic]]
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: OpStore %val4 [[result]]
|
||||||
|
float4 val4 = t2.Load(location, int2(1, 2), status);
|
||||||
|
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,18 @@ Texture2DMS <float> t7 : register(t7);
|
||||||
Texture2DMSArray<float3> t8 : register(t8);
|
Texture2DMSArray<float3> t8 : register(t8);
|
||||||
|
|
||||||
// CHECK: OpCapability ImageGatherExtended
|
// CHECK: OpCapability ImageGatherExtended
|
||||||
|
// CHECK: OpCapability SparseResidency
|
||||||
|
|
||||||
|
// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float
|
||||||
|
// CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4int
|
||||||
|
// CHECK: %SparseResidencyStruct_1 = OpTypeStruct %uint %v4uint
|
||||||
|
|
||||||
// CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
|
// CHECK: [[v2ic:%\d+]] = OpConstantComposite %v2int %int_1 %int_2
|
||||||
// CHECK: [[v4ic:%\d+]] = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
|
// CHECK: [[v4ic:%\d+]] = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
|
||||||
// CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
|
// CHECK: [[v3ic:%\d+]] = OpConstantComposite %v3int %int_3 %int_3 %int_3
|
||||||
|
|
||||||
float4 main(int3 location: A, int offset: B) : SV_Target {
|
float4 main(int3 location: A, int offset: B) : SV_Target {
|
||||||
|
uint status;
|
||||||
|
|
||||||
// CHECK: [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
|
// CHECK: [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
|
||||||
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
|
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
|
||||||
|
@ -88,5 +94,67 @@ float4 main(int3 location: A, int offset: B) : SV_Target {
|
||||||
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3float [[f81]] [[f81]] 0 1 2
|
// CHECK-NEXT: {{%\d+}} = OpVectorShuffle %v3float [[f81]] [[f81]] 0 1 2
|
||||||
val8 = t8.Load(pos3, sampleIndex, int2(1,2));
|
val8 = t8.Load(pos3, sampleIndex, int2(1,2));
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
/// Using the Status argument ///
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
// CHECK: [[coord:%\d+]] = OpCompositeExtract %int [[v2ic]] 0
|
||||||
|
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[v2ic]] 1
|
||||||
|
// CHECK-NEXT: [[offset:%\d+]] = OpLoad %int %offset
|
||||||
|
// CHECK-NEXT: [[t4:%\d+]] = OpLoad %type_1d_image %t4
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t4]] [[coord]] Lod|Offset [[lod]] [[offset]]
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpCompositeExtract %float [[v4result]] 0
|
||||||
|
// CHECK-NEXT: OpStore %val14 [[result]]
|
||||||
|
float val14 = t4.Load(int2(1,2), offset, status);
|
||||||
|
|
||||||
|
// CHECK: [[loc:%\d+]] = OpLoad %v3int %location
|
||||||
|
// CHECK-NEXT: [[coord:%\d+]] = OpVectorShuffle %v2int [[loc]] [[loc]] 0 1
|
||||||
|
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[loc]] 2
|
||||||
|
// CHECK-NEXT: [[t5:%\d+]] = OpLoad %type_2d_image_0 %t5
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct_0 [[t5]] [[coord]] Lod|ConstOffset [[lod]] [[v2ic]]
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4int [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v2int [[v4result]] [[v4result]] 0 1
|
||||||
|
// CHECK-NEXT: OpStore %val15 [[result]]
|
||||||
|
int2 val15 = t5.Load(location, int2(1,2), status);
|
||||||
|
|
||||||
|
// CHECK: [[coord:%\d+]] = OpVectorShuffle %v3int [[v4ic]] [[v4ic]] 0 1 2
|
||||||
|
// CHECK-NEXT: [[lod:%\d+]] = OpCompositeExtract %int [[v4ic]] 3
|
||||||
|
// CHECK-NEXT: [[t6:%\d+]] = OpLoad %type_3d_image_0 %t6
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct_1 [[t6]] [[coord]] Lod|ConstOffset [[lod]] [[v3ic]]
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4uint [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v3uint [[v4result]] [[v4result]] 0 1 2
|
||||||
|
// CHECK-NEXT: OpStore %val16 [[result]]
|
||||||
|
uint3 val16 = t6.Load(int4(1, 2, 3, 4), 3, status);
|
||||||
|
|
||||||
|
// CHECK: [[pos1:%\d+]] = OpLoad %v2int %pos2
|
||||||
|
// CHECK-NEXT: [[si1:%\d+]] = OpLoad %int %sampleIndex
|
||||||
|
// CHECK-NEXT: [[offset2:%\d+]] = OpLoad %v2int %offset2
|
||||||
|
// CHECK-NEXT: [[t71:%\d+]] = OpLoad %type_2d_image_1 %t7
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t71]] [[pos1]] Offset|Sample [[offset2]] [[si1]]
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpCompositeExtract %float [[v4result]] 0
|
||||||
|
// CHECK-NEXT: OpStore %val17 [[result]]
|
||||||
|
float val17 = t7.Load(pos2, sampleIndex, offset2, status);
|
||||||
|
|
||||||
|
// CHECK: [[pos3:%\d+]] = OpLoad %v3int %pos3
|
||||||
|
// CHECK-NEXT: [[si3:%\d+]] = OpLoad %int %sampleIndex
|
||||||
|
// CHECK-NEXT: [[t81:%\d+]] = OpLoad %type_2d_image_array %t8
|
||||||
|
// CHECK-NEXT:[[structResult:%\d+]] = OpImageSparseFetch %SparseResidencyStruct [[t81]] [[pos3]] ConstOffset|Sample [[v2ic]] [[si3]]
|
||||||
|
// CHECK-NEXT: [[status:%\d+]] = OpCompositeExtract %uint [[structResult]] 0
|
||||||
|
// CHECK-NEXT: OpStore %status [[status]]
|
||||||
|
// CHECK-NEXT: [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
|
||||||
|
// CHECK-NEXT: [[result:%\d+]] = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
|
||||||
|
// CHECK-NEXT: OpStore %val18 [[result]]
|
||||||
|
float3 val18 = t8.Load(pos3, sampleIndex, int2(1,2), status);
|
||||||
|
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче