shader/image: Implement SUATOM and fix SUST
This commit is contained in:
parent
34b2c60f95
commit
36abf67e79
7 changed files with 332 additions and 72 deletions
|
@ -44,7 +44,6 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
|
|||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::SUST: {
|
||||
UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P);
|
||||
UNIMPLEMENTED_IF(instr.sust.image_type == Tegra::Shader::ImageType::TextureBuffer);
|
||||
UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore);
|
||||
UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store
|
||||
|
||||
|
@ -66,8 +65,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
|
|||
image.MarkWrite();
|
||||
|
||||
MetaImage meta{image, values};
|
||||
const Node store{Operation(OperationCode::ImageStore, meta, std::move(coords))};
|
||||
bb.push_back(store);
|
||||
bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords)));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SUATOM: {
|
||||
UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0);
|
||||
|
||||
Node value = GetRegister(instr.gpr0);
|
||||
|
||||
std::vector<Node> coords;
|
||||
const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
|
||||
for (std::size_t i = 0; i < num_coords; ++i) {
|
||||
coords.push_back(GetRegister(instr.gpr8.Value() + i));
|
||||
}
|
||||
|
||||
const OperationCode operation_code = [instr] {
|
||||
switch (instr.suatom_d.operation) {
|
||||
case Tegra::Shader::ImageAtomicOperation::Add:
|
||||
return OperationCode::AtomicImageAdd;
|
||||
case Tegra::Shader::ImageAtomicOperation::Min:
|
||||
return OperationCode::AtomicImageMin;
|
||||
case Tegra::Shader::ImageAtomicOperation::Max:
|
||||
return OperationCode::AtomicImageMax;
|
||||
case Tegra::Shader::ImageAtomicOperation::And:
|
||||
return OperationCode::AtomicImageAnd;
|
||||
case Tegra::Shader::ImageAtomicOperation::Or:
|
||||
return OperationCode::AtomicImageOr;
|
||||
case Tegra::Shader::ImageAtomicOperation::Xor:
|
||||
return OperationCode::AtomicImageXor;
|
||||
case Tegra::Shader::ImageAtomicOperation::Exch:
|
||||
return OperationCode::AtomicImageExchange;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented operation={}",
|
||||
static_cast<u32>(instr.suatom_d.operation.Value()));
|
||||
return OperationCode::AtomicImageAdd;
|
||||
}
|
||||
}();
|
||||
|
||||
const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)};
|
||||
MetaImage meta{image, {std::move(value)}};
|
||||
SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -77,38 +114,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
|
|||
return pc;
|
||||
}
|
||||
|
||||
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
|
||||
const auto offset{static_cast<u64>(image.index.Value())};
|
||||
|
||||
// If this image has already been used, return the existing mapping.
|
||||
const auto it = used_images.find(offset);
|
||||
if (it != used_images.end()) {
|
||||
ASSERT(it->second.GetType() == type);
|
||||
return it->second;
|
||||
Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size) {
|
||||
const auto offset{static_cast<std::size_t>(image.index.Value())};
|
||||
if (const auto image = TryUseExistingImage(offset, type, size)) {
|
||||
return *image;
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this image.
|
||||
const std::size_t next_index{used_images.size()};
|
||||
return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
|
||||
return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second;
|
||||
}
|
||||
|
||||
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
|
||||
Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size) {
|
||||
const Node image_register{GetRegister(reg)};
|
||||
const auto [base_image, cbuf_index, cbuf_offset]{
|
||||
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
|
||||
const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
|
||||
|
||||
// If this image has already been used, return the existing mapping.
|
||||
const auto it = used_images.find(cbuf_key);
|
||||
if (it != used_images.end()) {
|
||||
ASSERT(it->second.GetType() == type);
|
||||
return it->second;
|
||||
if (const auto image = TryUseExistingImage(cbuf_key, type, size)) {
|
||||
return *image;
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this image.
|
||||
const std::size_t next_index{used_images.size()};
|
||||
return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
|
||||
return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size})
|
||||
.first->second;
|
||||
}
|
||||
|
||||
Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size) {
|
||||
auto it = used_images.find(offset);
|
||||
if (it == used_images.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& image = it->second;
|
||||
ASSERT(image.GetType() == type);
|
||||
|
||||
if (size) {
|
||||
// We know the size, if it's known it has to be the same as before, otherwise we can set it.
|
||||
if (image.IsSizeKnown()) {
|
||||
ASSERT(image.GetSize() == size);
|
||||
} else {
|
||||
image.SetSize(*size);
|
||||
}
|
||||
}
|
||||
return ℑ
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
@ -148,7 +149,14 @@ enum class OperationCode {
|
|||
TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TexelFetch, /// (MetaTexture, int[N], int) -> float4
|
||||
|
||||
ImageStore, /// (MetaImage, float[N] coords) -> void
|
||||
ImageStore, /// (MetaImage, int[N] values) -> void
|
||||
AtomicImageAdd, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageMin, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageMax, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageAnd, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageOr, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageXor, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageExchange, /// (MetaImage, int[N] coords) -> void
|
||||
|
||||
Branch, /// (uint branch_target) -> void
|
||||
BranchIndirect, /// (uint branch_target) -> void
|
||||
|
@ -275,25 +283,32 @@ private:
|
|||
|
||||
class Image final {
|
||||
public:
|
||||
constexpr explicit Image(u64 offset, std::size_t index, Tegra::Shader::ImageType type)
|
||||
: offset{offset}, index{index}, type{type}, is_bindless{false} {}
|
||||
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size)
|
||||
: offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {}
|
||||
|
||||
constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
|
||||
Tegra::Shader::ImageType type)
|
||||
Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size)
|
||||
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
|
||||
is_bindless{true} {}
|
||||
is_bindless{true}, size{size} {}
|
||||
|
||||
constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
|
||||
bool is_bindless, bool is_written, bool is_read)
|
||||
bool is_bindless, bool is_written, bool is_read,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size)
|
||||
: offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
|
||||
is_written{is_written}, is_read{is_read} {}
|
||||
is_written{is_written}, is_read{is_read}, size{size} {}
|
||||
|
||||
void MarkWrite() {
|
||||
is_written = true;
|
||||
}
|
||||
|
||||
void MarkRead() {
|
||||
is_read = true;
|
||||
}
|
||||
|
||||
void MarkWrite() {
|
||||
is_written = true;
|
||||
void SetSize(Tegra::Shader::ImageAtomicSize size_) {
|
||||
size = size_;
|
||||
}
|
||||
|
||||
constexpr std::size_t GetOffset() const {
|
||||
|
@ -312,25 +327,39 @@ public:
|
|||
return is_bindless;
|
||||
}
|
||||
|
||||
constexpr bool IsRead() const {
|
||||
return is_read;
|
||||
}
|
||||
|
||||
constexpr bool IsWritten() const {
|
||||
return is_written;
|
||||
}
|
||||
|
||||
constexpr bool IsRead() const {
|
||||
return is_read;
|
||||
}
|
||||
|
||||
constexpr std::pair<u32, u32> GetBindlessCBuf() const {
|
||||
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
|
||||
}
|
||||
|
||||
constexpr bool IsSizeKnown() const {
|
||||
return size.has_value();
|
||||
}
|
||||
|
||||
constexpr Tegra::Shader::ImageAtomicSize GetSize() const {
|
||||
return size.value();
|
||||
}
|
||||
|
||||
constexpr bool operator<(const Image& rhs) const {
|
||||
return std::tie(offset, index, type, size, is_bindless) <
|
||||
std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless);
|
||||
}
|
||||
|
||||
private:
|
||||
u64 offset{};
|
||||
std::size_t index{};
|
||||
Tegra::Shader::ImageType type{};
|
||||
bool is_bindless{};
|
||||
bool is_read{};
|
||||
bool is_written{};
|
||||
bool is_read{};
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size{};
|
||||
};
|
||||
|
||||
struct GlobalMemoryBase {
|
||||
|
|
|
@ -272,10 +272,16 @@ private:
|
|||
bool is_shadow);
|
||||
|
||||
/// Accesses an image.
|
||||
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
|
||||
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size = {});
|
||||
|
||||
/// Access a bindless image sampler.
|
||||
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
|
||||
Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size = {});
|
||||
|
||||
/// Tries to access an existing image, updating it's state as needed
|
||||
Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
|
||||
std::optional<Tegra::Shader::ImageAtomicSize> size);
|
||||
|
||||
/// Extracts a sequence of bits from a node
|
||||
Node BitfieldExtract(Node value, u32 offset, u32 bits);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue