Compare commits

...
Author SHA1 Message Date
David b9fd1e2bed Merge pull request #2879 from ogniK5377/trace-submitgpfifo
Mark KickOffPb & SubmitGPFIFO as trace
2019-09-21 16:22:39 +10:00
David 41836f3a17 Merge pull request #2880 from ogniK5377/drawarrays-trace
Mark DrawArrays as LOG_TRACE
2019-09-21 16:11:54 +10:00
David Marcec 01a4afee42 Mark DrawArrays as LOG_TRACE
There's no reason to clog logs with DrawArray.
2019-09-21 15:43:58 +10:00
David Marcec c2f966dbc1 Mark KickOffPb & SubmitGPFIFO as trace
These functions are not stubbed and are called fairly often. Due to the nature of how often they're called, we should keep them marked as LOG_TRACE instead of LOG_DEBUG or LOG_WARNING
2019-09-21 15:41:11 +10:00
bunnei bbe82d62b0 Merge pull request #2846 from ReinUsesLisp/fixup-viewport-index
gl_shader_decompiler: Avoid writing output attribute when unimplemented
2019-09-20 17:11:20 -04:00
bunnei 88d857499b Merge pull request #2855 from ReinUsesLisp/shfl
shader_ir/warp: Implement SHFL for Nvidia devices
2019-09-20 17:10:42 -04:00
bunnei b31880dc5e Merge pull request #2784 from ReinUsesLisp/smem
shader_ir: Implement shared memory
2019-09-18 16:26:05 -04:00
ReinUsesLisp 0526bf1895 shader_ir/warp: Implement SHFL 2019-09-17 17:44:07 -03:00
Fernando Sahmkow 393cc3ef2f Merge pull request #2851 from ReinUsesLisp/srgb
renderer_opengl: Fix sRGB blits
2019-09-15 10:38:10 -04:00
ReinUsesLisp 6b997c8f7f renderer_opengl: Fix rebase mistake 2019-09-11 00:09:37 -03:00
Fernando Sahmkow e60d281a01 gl_rasterizer: Correct sRGB Fix regression 2019-09-10 19:31:42 -03:00
ReinUsesLisp 78574746bd renderer_opengl: Fix sRGB blits
Removes the sRGB hack of tracking if a frame used an sRGB rendertarget
to apply at least once to blit the final texture as sRGB. Instead of
doing this apply sRGB if the presented image has sRGB.

Also enable sRGB by default on Maxwell3D registers as some games seem to
assume this.
2019-09-10 19:31:42 -03:00
ReinUsesLisp 17a9b0178d gl_shader_decompiler: Avoid writing output attribute when unimplemented 2019-09-06 15:02:12 -03:00
ReinUsesLisp 0f7b813d65 gl_shader_decompiler: Implement shared memory 2019-09-05 01:40:24 -03:00
ReinUsesLisp 4de04eba39 shader_ir: Implement LD_S
Loads from shared memory.
2019-09-05 01:38:37 -03:00
ReinUsesLisp f17415d431 shader_ir: Implement ST_S
This instruction writes to a memory buffer shared with threads within
the same work group. It is known as "shared" memory in GLSL.
2019-09-05 01:38:37 -03:00
16 changed files with 291 additions and 75 deletions

No files matched your search

@@ -146,8 +146,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.address, params.num_entries, params.flags.raw);
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -179,8 +179,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.address, params.num_entries, params.flags.raw);
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
Tegra::CommandList entries(params.num_entries);
Memory::ReadBlock(params.address, entries.data(),
+4 -1
View File
@@ -89,6 +89,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
// Commercial games seem to assume this value is enabled and nouveau sets this value manually.
regs.rt_separate_frag_data = 1;
// Some games (like Super Mario Odyssey) assume that SRGB is enabled.
regs.framebuffer_srgb = 1;
}
#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name))
@@ -538,7 +541,7 @@ void Maxwell3D::ProcessSyncPoint() {
}
void Maxwell3D::DrawArrays() {
LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
LOG_TRACE(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
+18
View File
@@ -566,6 +566,13 @@ enum class ImageAtomicOperation : u64 {
Exch = 8,
};
enum class ShuffleOperation : u64 {
Idx = 0, // shuffleNV
Up = 1, // shuffleUpNV
Down = 2, // shuffleDownNV
Bfly = 3, // shuffleXorNV
};
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -599,6 +606,15 @@ union Instruction {
BitField<42, 1, u64> negate_value;
} vote;
union {
BitField<30, 2, ShuffleOperation> operation;
BitField<48, 3, u64> pred48;
BitField<28, 1, u64> is_index_imm;
BitField<29, 1, u64> is_mask_imm;
BitField<20, 5, u64> index_imm;
BitField<34, 13, u64> mask_imm;
} shfl;
union {
BitField<8, 8, Register> gpr;
BitField<20, 24, s64> offset;
@@ -1542,6 +1558,7 @@ public:
BRK,
DEPBAR,
VOTE,
SHFL,
BFE_C,
BFE_R,
BFE_IMM,
@@ -1833,6 +1850,7 @@ private:
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
INST("1110111100010---", Id::SHFL, Type::Warp, "SHFL"),
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
@@ -489,9 +489,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
// Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it.
texture_cache.MarkColorBufferInUse(*single_color_target);
// Workaround for and issue in nvidia drivers
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
}
fbkey.is_single_buffer = true;
@@ -512,11 +509,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
// Assume that a surface will be written to if it is used as a framebuffer, even
// if the shader doesn't actually write to it.
texture_cache.MarkColorBufferInUse(index);
// Enable sRGB only for supported formats
// Workaround for and issue in nvidia drivers
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
state.framebuffer_srgb.enabled |=
color_surface->GetSurfaceParams().srgb_conversion;
}
fbkey.color_attachments[index] =
@@ -906,6 +898,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
}
screen_info.display_texture = surface->GetTexture();
screen_info.display_srgb = surface->GetSurfaceParams().srgb_conversion;
return true;
}
@@ -214,7 +214,8 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn
std::string source = "#version 430 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_NV_gpu_shader5 : enable\n"
"#extension GL_NV_shader_thread_group : enable\n";
"#extension GL_NV_shader_thread_group : enable\n"
"#extension GL_NV_shader_thread_shuffle : enable\n";
if (entries.shader_viewport_layer_array) {
source += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
}
@@ -325,6 +325,7 @@ public:
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareSharedMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
@@ -499,6 +500,13 @@ private:
code.AddNewLine();
}
void DeclareSharedMemory() {
if (stage != ProgramType::Compute) {
return;
}
code.AddLine("shared uint {}[];", GetSharedMemory());
}
void DeclareInternalFlags() {
for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
const auto flag_code = static_cast<InternalFlag>(flag);
@@ -881,6 +889,12 @@ private:
Type::Uint};
}
if (const auto smem = std::get_if<SmemNode>(&*node)) {
return {
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
Type::Uint};
}
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
return {GetInternalFlag(internal_flag->GetFlag()), Type::Bool};
}
@@ -1007,10 +1021,10 @@ private:
return {std::move(temporary), value.GetType()};
}
Expression GetOutputAttribute(const AbufNode* abuf) {
std::optional<Expression> GetOutputAttribute(const AbufNode* abuf) {
switch (const auto attribute = abuf->GetIndex()) {
case Attribute::Index::Position:
return {"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float};
return {{"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float}};
case Attribute::Index::LayerViewportPointSize:
switch (abuf->GetElement()) {
case 0:
@@ -1020,25 +1034,25 @@ private:
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
return {};
}
return {"gl_Layer", Type::Int};
return {{"gl_Layer", Type::Int}};
case 2:
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
return {};
}
return {"gl_ViewportIndex", Type::Int};
return {{"gl_ViewportIndex", Type::Int}};
case 3:
UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
return {"gl_PointSize", Type::Float};
return {{"gl_PointSize", Type::Float}};
}
return {};
case Attribute::Index::ClipDistances0123:
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float};
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float}};
case Attribute::Index::ClipDistances4567:
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float};
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}};
default:
if (IsGenericAttribute(attribute)) {
return {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()),
Type::Float};
return {
{GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), Type::Float}};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
return {};
@@ -1278,7 +1292,11 @@ private:
target = {GetRegister(gpr->GetIndex()), Type::Float};
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
target = GetOutputAttribute(abuf);
auto output = GetOutputAttribute(abuf);
if (!output) {
return {};
}
target = std::move(*output);
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
if (stage == ProgramType::Compute) {
LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
@@ -1286,6 +1304,11 @@ private:
target = {
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
Type::Uint};
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
ASSERT(stage == ProgramType::Compute);
target = {
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
Type::Uint};
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
@@ -1934,8 +1957,7 @@ private:
Expression BallotThread(Operation operation) {
const std::string value = VisitOperand(operation, 0).AsBool();
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL,
"Nvidia warp intrinsics are not available and its required by a shader");
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
// Stub on non-Nvidia devices by simulating all threads voting the same as the active
// one.
return {fmt::format("({} ? 0xFFFFFFFFU : 0U)", value), Type::Uint};
@@ -1946,8 +1968,7 @@ private:
Expression Vote(Operation operation, const char* func) {
const std::string value = VisitOperand(operation, 0).AsBool();
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL,
"Nvidia vote intrinsics are not available and its required by a shader");
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
// Stub with a warp size of one.
return {value, Type::Bool};
}
@@ -1964,15 +1985,54 @@ private:
Expression VoteEqual(Operation operation) {
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL,
"Nvidia vote intrinsics are not available and its required by a shader");
// We must return true here since a stub for a theoretical warp size of 1 will always
// return an equal result for all its votes.
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
// We must return true here since a stub for a theoretical warp size of 1.
// This will always return an equal result across all votes.
return {"true", Type::Bool};
}
return Vote(operation, "allThreadsEqualNV");
}
template <const std::string_view& func>
Expression Shuffle(Operation operation) {
const std::string value = VisitOperand(operation, 0).AsFloat();
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL, "Nvidia shuffle intrinsics are required by this shader");
// On a "single-thread" device we are either on the same thread or out of bounds. Both
// cases return the passed value.
return {value, Type::Float};
}
const std::string index = VisitOperand(operation, 1).AsUint();
const std::string width = VisitOperand(operation, 2).AsUint();
return {fmt::format("{}({}, {}, {})", func, value, index, width), Type::Float};
}
template <const std::string_view& func>
Expression InRangeShuffle(Operation operation) {
const std::string index = VisitOperand(operation, 0).AsUint();
const std::string width = VisitOperand(operation, 1).AsUint();
if (!device.HasWarpIntrinsics()) {
// On a "single-thread" device we are only in bounds when the requested index is 0.
return {fmt::format("({} == 0U)", index), Type::Bool};
}
const std::string in_range = code.GenerateTemporary();
code.AddLine("bool {};", in_range);
code.AddLine("{}(0U, {}, {}, {});", func, index, width, in_range);
return {in_range, Type::Bool};
}
struct Func final {
Func() = delete;
~Func() = delete;
static constexpr std::string_view ShuffleIndexed = "shuffleNV";
static constexpr std::string_view ShuffleUp = "shuffleUpNV";
static constexpr std::string_view ShuffleDown = "shuffleDownNV";
static constexpr std::string_view ShuffleButterfly = "shuffleXorNV";
};
static constexpr std::array operation_decompilers = {
&GLSLDecompiler::Assign,
@@ -2135,6 +2195,16 @@ private:
&GLSLDecompiler::VoteAll,
&GLSLDecompiler::VoteAny,
&GLSLDecompiler::VoteEqual,
&GLSLDecompiler::Shuffle<Func::ShuffleIndexed>,
&GLSLDecompiler::Shuffle<Func::ShuffleUp>,
&GLSLDecompiler::Shuffle<Func::ShuffleDown>,
&GLSLDecompiler::Shuffle<Func::ShuffleButterfly>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleIndexed>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleUp>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleDown>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleButterfly>,
};
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
@@ -2175,6 +2245,10 @@ private:
return "lmem_" + suffix;
}
std::string GetSharedMemory() const {
return fmt::format("smem_{}", suffix);
}
std::string GetInternalFlag(InternalFlag flag) const {
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
"overflow_flag"};
@@ -16,7 +16,6 @@ namespace OpenGL {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
OpenGLState OpenGLState::cur_state;
bool OpenGLState::s_rgb_used;
namespace {
@@ -282,8 +281,6 @@ void OpenGLState::ApplySRgb() const {
return;
cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
if (framebuffer_srgb.enabled) {
// Track if sRGB is used
s_rgb_used = true;
glEnable(GL_FRAMEBUFFER_SRGB);
} else {
glDisable(GL_FRAMEBUFFER_SRGB);
-10
View File
@@ -175,14 +175,6 @@ public:
return cur_state;
}
static bool GetsRGBUsed() {
return s_rgb_used;
}
static void ClearsRGBUsed() {
s_rgb_used = false;
}
void SetDefaultViewports();
/// Apply this state as the current OpenGL state
void Apply();
@@ -253,8 +245,6 @@ public:
private:
static OpenGLState cur_state;
// Workaround for sRGB problems caused by QT not supporting srgb output
static bool s_rgb_used;
struct {
bool blend_state;
bool stencil_state;
@@ -264,7 +264,6 @@ void RendererOpenGL::CreateRasterizer() {
if (rasterizer) {
return;
}
OpenGLState::ClearsRGBUsed();
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
}
@@ -343,9 +342,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
}};
state.textures[0] = screen_info.display_texture;
// Workaround brigthness problems in SMO by enabling sRGB in the final output
// if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
state.framebuffer_srgb.enabled = screen_info.display_srgb;
state.AllDirty();
state.Apply();
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), vertices.data());
@@ -355,8 +352,6 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
state.textures[0] = 0;
state.AllDirty();
state.Apply();
// Clear sRGB state for the next frame
OpenGLState::ClearsRGBUsed();
}
/**
@@ -406,8 +401,8 @@ void RendererOpenGL::CaptureScreenshot() {
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, state.GetsRGBUsed() ? GL_SRGB8 : GL_RGB8, layout.width,
layout.height);
glRenderbufferStorage(GL_RENDERBUFFER, screen_info.display_srgb ? GL_SRGB8 : GL_RGB8,
layout.width, layout.height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
DrawScreen(layout);
@@ -38,7 +38,8 @@ struct TextureInfo {
/// Structure used for storing information about the display target for the Switch screen
struct ScreenInfo {
GLuint display_texture;
GLuint display_texture{};
bool display_srgb{};
const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
TextureInfo texture;
};
@@ -1127,6 +1127,46 @@ private:
return {};
}
Id ShuffleIndexed(Operation) {
UNIMPLEMENTED();
return {};
}
Id ShuffleUp(Operation) {
UNIMPLEMENTED();
return {};
}
Id ShuffleDown(Operation) {
UNIMPLEMENTED();
return {};
}
Id ShuffleButterfly(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleIndexed(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleUp(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleDown(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleButterfly(Operation) {
UNIMPLEMENTED();
return {};
}
Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type,
const std::string& name) {
const Id id = OpVariable(type, storage);
@@ -1431,6 +1471,16 @@ private:
&SPIRVDecompiler::VoteAll,
&SPIRVDecompiler::VoteAny,
&SPIRVDecompiler::VoteEqual,
&SPIRVDecompiler::ShuffleIndexed,
&SPIRVDecompiler::ShuffleUp,
&SPIRVDecompiler::ShuffleDown,
&SPIRVDecompiler::ShuffleButterfly,
&SPIRVDecompiler::InRangeShuffleIndexed,
&SPIRVDecompiler::InRangeShuffleUp,
&SPIRVDecompiler::InRangeShuffleDown,
&SPIRVDecompiler::InRangeShuffleButterfly,
};
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
+29 -19
View File
@@ -35,7 +35,7 @@ u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) {
return 1;
}
}
} // namespace
} // Anonymous namespace
u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
@@ -106,16 +106,17 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
}
break;
}
case OpCode::Id::LD_L: {
LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}",
static_cast<u64>(instr.ld_l.unknown.Value()));
const auto GetLmem = [&](s32 offset) {
case OpCode::Id::LD_L:
LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown));
[[fallthrough]];
case OpCode::Id::LD_S: {
const auto GetMemory = [&](s32 offset) {
ASSERT(offset % 4 == 0);
const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset);
const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8),
immediate_offset);
return GetLocalMemory(address);
return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address)
: GetLocalMemory(address);
};
switch (instr.ldst_sl.type.Value()) {
@@ -135,14 +136,16 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
return 0;
}
}();
for (u32 i = 0; i < count; ++i)
SetTemporary(bb, i, GetLmem(i * 4));
for (u32 i = 0; i < count; ++i)
for (u32 i = 0; i < count; ++i) {
SetTemporary(bb, i, GetMemory(i * 4));
}
for (u32 i = 0; i < count; ++i) {
SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
}
break;
}
default:
UNIMPLEMENTED_MSG("LD_L Unhandled type: {}",
UNIMPLEMENTED_MSG("{} Unhandled type: {}", opcode->get().GetName(),
static_cast<u32>(instr.ldst_sl.type.Value()));
}
break;
@@ -209,27 +212,34 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::ST_L: {
case OpCode::Id::ST_L:
LOG_DEBUG(HW_GPU, "ST_L cache management mode: {}",
static_cast<u64>(instr.st_l.cache_management.Value()));
const auto GetLmemAddr = [&](s32 offset) {
[[fallthrough]];
case OpCode::Id::ST_S: {
const auto GetAddress = [&](s32 offset) {
ASSERT(offset % 4 == 0);
const Node immediate = Immediate(static_cast<s32>(instr.smem_imm) + offset);
return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate);
};
const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L
? &ShaderIR::SetLocalMemory
: &ShaderIR::SetSharedMemory;
switch (instr.ldst_sl.type.Value()) {
case Tegra::Shader::StoreType::Bits128:
SetLocalMemory(bb, GetLmemAddr(12), GetRegister(instr.gpr0.Value() + 3));
SetLocalMemory(bb, GetLmemAddr(8), GetRegister(instr.gpr0.Value() + 2));
(this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3));
(this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2));
[[fallthrough]];
case Tegra::Shader::StoreType::Bits64:
SetLocalMemory(bb, GetLmemAddr(4), GetRegister(instr.gpr0.Value() + 1));
(this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1));
[[fallthrough]];
case Tegra::Shader::StoreType::Bits32:
SetLocalMemory(bb, GetLmemAddr(0), GetRegister(instr.gpr0));
(this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0));
break;
default:
UNIMPLEMENTED_MSG("ST_L Unhandled type: {}",
UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(),
static_cast<u32>(instr.ldst_sl.type.Value()));
}
break;
+47
View File
@@ -13,6 +13,7 @@ namespace VideoCommon::Shader {
using Tegra::Shader::Instruction;
using Tegra::Shader::OpCode;
using Tegra::Shader::Pred;
using Tegra::Shader::ShuffleOperation;
using Tegra::Shader::VoteOperation;
namespace {
@@ -44,6 +45,52 @@ u32 ShaderIR::DecodeWarp(NodeBlock& bb, u32 pc) {
SetPredicate(bb, instr.vote.dest_pred, vote);
break;
}
case OpCode::Id::SHFL: {
Node mask = instr.shfl.is_mask_imm ? Immediate(static_cast<u32>(instr.shfl.mask_imm))
: GetRegister(instr.gpr39);
Node width = [&] {
// Convert the obscure SHFL mask back into GL_NV_shader_thread_shuffle's width. This has
// been done reversing Nvidia's math. It won't work on all cases due to SHFL having
// different parameters that don't properly map to GLSL's interface, but it should work
// for cases emitted by Nvidia's compiler.
if (instr.shfl.operation == ShuffleOperation::Up) {
return Operation(
OperationCode::ILogicalShiftRight,
Operation(OperationCode::IAdd, std::move(mask), Immediate(-0x2000)),
Immediate(8));
} else {
return Operation(OperationCode::ILogicalShiftRight,
Operation(OperationCode::IAdd, Immediate(0x201F),
Operation(OperationCode::INegate, std::move(mask))),
Immediate(8));
}
}();
const auto [operation, in_range] = [instr]() -> std::pair<OperationCode, OperationCode> {
switch (instr.shfl.operation) {
case ShuffleOperation::Idx:
return {OperationCode::ShuffleIndexed, OperationCode::InRangeShuffleIndexed};
case ShuffleOperation::Up:
return {OperationCode::ShuffleUp, OperationCode::InRangeShuffleUp};
case ShuffleOperation::Down:
return {OperationCode::ShuffleDown, OperationCode::InRangeShuffleDown};
case ShuffleOperation::Bfly:
return {OperationCode::ShuffleButterfly, OperationCode::InRangeShuffleButterfly};
}
UNREACHABLE_MSG("Invalid SHFL operation: {}",
static_cast<u64>(instr.shfl.operation.Value()));
return {};
}();
// Setting the predicate before the register is intentional to avoid overwriting.
Node index = instr.shfl.is_index_imm ? Immediate(static_cast<u32>(instr.shfl.index_imm))
: GetRegister(instr.gpr20);
SetPredicate(bb, instr.shfl.pred48, Operation(in_range, index, width));
SetRegister(
bb, instr.gpr0,
Operation(operation, GetRegister(instr.gpr8), std::move(index), std::move(width)));
break;
}
default:
UNIMPLEMENTED_MSG("Unhandled warp instruction: {}", opcode->get().GetName());
break;
+25 -1
View File
@@ -181,6 +181,16 @@ enum class OperationCode {
VoteAny, /// (bool) -> bool
VoteEqual, /// (bool) -> bool
ShuffleIndexed, /// (uint value, uint index, uint width) -> uint
ShuffleUp, /// (uint value, uint index, uint width) -> uint
ShuffleDown, /// (uint value, uint index, uint width) -> uint
ShuffleButterfly, /// (uint value, uint index, uint width) -> uint
InRangeShuffleIndexed, /// (uint index, uint width) -> bool
InRangeShuffleUp, /// (uint index, uint width) -> bool
InRangeShuffleDown, /// (uint index, uint width) -> bool
InRangeShuffleButterfly, /// (uint index, uint width) -> bool
Amount,
};
@@ -206,12 +216,13 @@ class PredicateNode;
class AbufNode;
class CbufNode;
class LmemNode;
class SmemNode;
class GmemNode;
class CommentNode;
using NodeData =
std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode,
PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>;
PredicateNode, AbufNode, CbufNode, LmemNode, SmemNode, GmemNode, CommentNode>;
using Node = std::shared_ptr<NodeData>;
using Node4 = std::array<Node, 4>;
using NodeBlock = std::vector<Node>;
@@ -583,6 +594,19 @@ private:
Node address;
};
/// Shared memory node
class SmemNode final {
public:
explicit SmemNode(Node address) : address{std::move(address)} {}
const Node& GetAddress() const {
return address;
}
private:
Node address;
};
/// Global memory node
class GmemNode final {
public:
+9
View File
@@ -137,6 +137,10 @@ Node ShaderIR::GetLocalMemory(Node address) {
return MakeNode<LmemNode>(std::move(address));
}
Node ShaderIR::GetSharedMemory(Node address) {
return MakeNode<SmemNode>(std::move(address));
}
Node ShaderIR::GetTemporary(u32 id) {
return GetRegister(Register::ZeroIndex + 1 + id);
}
@@ -378,6 +382,11 @@ void ShaderIR::SetLocalMemory(NodeBlock& bb, Node address, Node value) {
Operation(OperationCode::Assign, GetLocalMemory(std::move(address)), std::move(value)));
}
void ShaderIR::SetSharedMemory(NodeBlock& bb, Node address, Node value) {
bb.push_back(
Operation(OperationCode::Assign, GetSharedMemory(std::move(address)), std::move(value)));
}
void ShaderIR::SetTemporary(NodeBlock& bb, u32 id, Node value) {
SetRegister(bb, Register::ZeroIndex + 1 + id, std::move(value));
}
+5 -1
View File
@@ -208,6 +208,8 @@ private:
Node GetInternalFlag(InternalFlag flag, bool negated = false);
/// Generates a node representing a local memory address
Node GetLocalMemory(Node address);
/// Generates a node representing a shared memory address
Node GetSharedMemory(Node address);
/// Generates a temporary, internally it uses a post-RZ register
Node GetTemporary(u32 id);
@@ -217,8 +219,10 @@ private:
void SetPredicate(NodeBlock& bb, u64 dest, Node src);
/// Sets an internal flag. src value must be a bool-evaluated node
void SetInternalFlag(NodeBlock& bb, InternalFlag flag, Node value);
/// Sets a local memory address. address and value must be a number-evaluated node
/// Sets a local memory address with a value.
void SetLocalMemory(NodeBlock& bb, Node address, Node value);
/// Sets a shared memory address with a value.
void SetSharedMemory(NodeBlock& bb, Node address, Node value);
/// Sets a temporary. Internally it uses a post-RZ register
void SetTemporary(NodeBlock& bb, u32 id, Node value);