summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/engines/fermi_2d.h14
-rw-r--r--src/video_core/engines/maxwell_3d.h41
-rw-r--r--src/video_core/engines/shader_bytecode.h148
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp35
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h46
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp388
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp84
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h7
-rw-r--r--src/video_core/textures/decoders.cpp16
-rw-r--r--src/video_core/textures/decoders.h6
-rw-r--r--src/video_core/textures/texture.h17
-rw-r--r--src/video_core/utils.h24
18 files changed, 803 insertions, 146 deletions
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 81d15c62a..2a6e8bbbb 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -36,9 +36,9 @@ public:
RenderTargetFormat format;
BitField<0, 1, u32> linear;
union {
- BitField<0, 4, u32> block_depth;
+ BitField<0, 4, u32> block_width;
BitField<4, 4, u32> block_height;
- BitField<8, 4, u32> block_width;
+ BitField<8, 4, u32> block_depth;
};
u32 depth;
u32 layer;
@@ -53,10 +53,20 @@ public:
address_low);
}
+ u32 BlockWidth() const {
+ // The block width is stored in log2 format.
+ return 1 << block_width;
+ }
+
u32 BlockHeight() const {
// The block height is stored in log2 format.
return 1 << block_height;
}
+
+ u32 BlockDepth() const {
+ // The block depth is stored in log2 format.
+ return 1 << block_depth;
+ }
};
static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4290da33f..c8d1b6478 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -347,6 +347,16 @@ public:
DecrWrap = 8,
};
+ enum class MemoryLayout : u32 {
+ Linear = 0,
+ BlockLinear = 1,
+ };
+
+ enum class InvMemoryLayout : u32 {
+ BlockLinear = 0,
+ Linear = 1,
+ };
+
struct Cull {
enum class FrontFace : u32 {
ClockWise = 0x0900,
@@ -432,7 +442,12 @@ public:
u32 width;
u32 height;
Tegra::RenderTargetFormat format;
- u32 block_dimensions;
+ union {
+ BitField<0, 3, u32> block_width;
+ BitField<4, 3, u32> block_height;
+ BitField<8, 3, u32> block_depth;
+ BitField<12, 1, InvMemoryLayout> type;
+ } memory_layout;
u32 array_mode;
u32 layer_stride;
u32 base_layer;
@@ -532,7 +547,21 @@ public:
INSERT_PADDING_WORDS(0x3);
s32 clear_stencil;
- INSERT_PADDING_WORDS(0x6C);
+ INSERT_PADDING_WORDS(0x17);
+
+ struct {
+ u32 enable;
+ union {
+ BitField<0, 16, u32> min_x;
+ BitField<16, 16, u32> max_x;
+ };
+ union {
+ BitField<0, 16, u32> min_y;
+ BitField<16, 16, u32> max_y;
+ };
+ } scissor_test;
+
+ INSERT_PADDING_WORDS(0x52);
s32 stencil_back_func_ref;
u32 stencil_back_mask;
@@ -548,7 +577,12 @@ public:
u32 address_high;
u32 address_low;
Tegra::DepthFormat format;
- u32 block_dimensions;
+ union {
+ BitField<0, 4, u32> block_width;
+ BitField<4, 4, u32> block_height;
+ BitField<8, 4, u32> block_depth;
+ BitField<20, 1, InvMemoryLayout> type;
+ } memory_layout;
u32 layer_stride;
GPUVAddr Address() const {
@@ -1002,6 +1036,7 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
ASSERT_REG_POSITION(clear_stencil, 0x368);
+ASSERT_REG_POSITION(scissor_test, 0x380);
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b1f137b9c..9a59b65b3 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 {
XHi = 3,
};
+enum class VmadType : u64 {
+ Size16_Low = 0,
+ Size16_High = 1,
+ Size32 = 2,
+ Invalid = 3,
+};
+
+enum class VmadShr : u64 {
+ Shr7 = 1,
+ Shr15 = 2,
+};
+
enum class XmadMode : u64 {
None = 0,
CLo = 1,
@@ -314,6 +326,15 @@ enum class TextureMiscMode : u64 {
PTP,
};
+enum class IsberdMode : u64 {
+ None = 0,
+ Patch = 1,
+ Prim = 2,
+ Attr = 3,
+};
+
+enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
+
enum class IpaInterpMode : u64 {
Linear = 0,
Perspective = 1,
@@ -340,6 +361,87 @@ struct IpaMode {
}
};
+enum class SystemVariable : u64 {
+ LaneId = 0x00,
+ VirtCfg = 0x02,
+ VirtId = 0x03,
+ Pm0 = 0x04,
+ Pm1 = 0x05,
+ Pm2 = 0x06,
+ Pm3 = 0x07,
+ Pm4 = 0x08,
+ Pm5 = 0x09,
+ Pm6 = 0x0a,
+ Pm7 = 0x0b,
+ OrderingTicket = 0x0f,
+ PrimType = 0x10,
+ InvocationId = 0x11,
+ Ydirection = 0x12,
+ ThreadKill = 0x13,
+ ShaderType = 0x14,
+ DirectBeWriteAddressLow = 0x15,
+ DirectBeWriteAddressHigh = 0x16,
+ DirectBeWriteEnabled = 0x17,
+ MachineId0 = 0x18,
+ MachineId1 = 0x19,
+ MachineId2 = 0x1a,
+ MachineId3 = 0x1b,
+ Affinity = 0x1c,
+ InvocationInfo = 0x1d,
+ WscaleFactorXY = 0x1e,
+ WscaleFactorZ = 0x1f,
+ Tid = 0x20,
+ TidX = 0x21,
+ TidY = 0x22,
+ TidZ = 0x23,
+ CtaParam = 0x24,
+ CtaIdX = 0x25,
+ CtaIdY = 0x26,
+ CtaIdZ = 0x27,
+ NtId = 0x28,
+ CirQueueIncrMinusOne = 0x29,
+ Nlatc = 0x2a,
+ SmSpaVersion = 0x2c,
+ MultiPassShaderInfo = 0x2d,
+ LwinHi = 0x2e,
+ SwinHi = 0x2f,
+ SwinLo = 0x30,
+ SwinSz = 0x31,
+ SmemSz = 0x32,
+ SmemBanks = 0x33,
+ LwinLo = 0x34,
+ LwinSz = 0x35,
+ LmemLosz = 0x36,
+ LmemHioff = 0x37,
+ EqMask = 0x38,
+ LtMask = 0x39,
+ LeMask = 0x3a,
+ GtMask = 0x3b,
+ GeMask = 0x3c,
+ RegAlloc = 0x3d,
+ CtxAddr = 0x3e, // .fmask = F_SM50
+ BarrierAlloc = 0x3e, // .fmask = F_SM60
+ GlobalErrorStatus = 0x40,
+ WarpErrorStatus = 0x42,
+ WarpErrorStatusClear = 0x43,
+ PmHi0 = 0x48,
+ PmHi1 = 0x49,
+ PmHi2 = 0x4a,
+ PmHi3 = 0x4b,
+ PmHi4 = 0x4c,
+ PmHi5 = 0x4d,
+ PmHi6 = 0x4e,
+ PmHi7 = 0x4f,
+ ClockLo = 0x50,
+ ClockHi = 0x51,
+ GlobalTimerLo = 0x52,
+ GlobalTimerHi = 0x53,
+ HwTaskId = 0x60,
+ CircularQueueEntryIndex = 0x61,
+ CircularQueueEntryAddressLow = 0x62,
+ CircularQueueEntryAddressHigh = 0x63,
+};
+
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -362,6 +464,7 @@ union Instruction {
BitField<48, 16, u64> opcode;
union {
+ BitField<20, 16, u64> imm20_16;
BitField<20, 19, u64> imm20_19;
BitField<20, 32, s64> imm20_32;
BitField<45, 1, u64> negate_b;
@@ -403,6 +506,10 @@ union Instruction {
}
} lop3;
+ u16 GetImm20_16() const {
+ return static_cast<u16>(imm20_16);
+ }
+
u32 GetImm20_19() const {
u32 imm{static_cast<u32>(imm20_19)};
imm <<= 12;
@@ -915,6 +1022,35 @@ union Instruction {
} bra;
union {
+ BitField<39, 1, u64> emit; // EmitVertex
+ BitField<40, 1, u64> cut; // EndPrimitive
+ } out;
+
+ union {
+ BitField<31, 1, u64> skew;
+ BitField<32, 1, u64> o;
+ BitField<33, 2, IsberdMode> mode;
+ BitField<47, 2, IsberdShift> shift;
+ } isberd;
+
+ union {
+ BitField<48, 1, u64> signed_a;
+ BitField<38, 1, u64> is_byte_chunk_a;
+ BitField<36, 2, VmadType> type_a;
+ BitField<36, 2, u64> byte_height_a;
+
+ BitField<49, 1, u64> signed_b;
+ BitField<50, 1, u64> use_register_b;
+ BitField<30, 1, u64> is_byte_chunk_b;
+ BitField<28, 2, VmadType> type_b;
+ BitField<28, 2, u64> byte_height_b;
+
+ BitField<51, 2, VmadShr> shr;
+ BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
+ BitField<47, 1, u64> cc;
+ } vmad;
+
+ union {
BitField<20, 16, u64> imm20_16;
BitField<36, 1, u64> product_shift_left;
BitField<37, 1, u64> merge_37;
@@ -936,6 +1072,10 @@ union Instruction {
BitField<36, 5, u64> index;
} cbuf36;
+ // Unsure about the size of this one.
+ // It's always used with a gpr0, so any size should be fine.
+ BitField<20, 8, SystemVariable> sys20;
+
BitField<47, 1, u64> generates_cc;
BitField<61, 1, u64> is_b_imm;
BitField<60, 1, u64> is_b_gpr;
@@ -975,6 +1115,9 @@ public:
TMML, // Texture Mip Map Level
EXIT,
IPA,
+ OUT_R, // Emit vertex/primitive
+ ISBERD,
+ VMAD,
FFMA_IMM, // Fused Multiply and Add
FFMA_CR,
FFMA_RC,
@@ -1034,6 +1177,7 @@ public:
MOV_C,
MOV_R,
MOV_IMM,
+ MOV_SYS,
MOV32_IMM,
SHL_C,
SHL_R,
@@ -1209,6 +1353,9 @@ private:
INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
+ INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
+ INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
+ INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1255,6 +1402,7 @@ private:
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
+ INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"),
INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 209bdf181..84582c777 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -255,7 +255,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
return params;
}
-void RasterizerOpenGL::SetupShaders() {
+void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
MICROPROFILE_SCOPE(OpenGL_Shader);
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
@@ -270,6 +270,11 @@ void RasterizerOpenGL::SetupShaders() {
// Skip stages that are not enabled
if (!gpu.regs.IsShaderConfigEnabled(index)) {
+ switch (program) {
+ case Maxwell::ShaderProgram::Geometry:
+ shader_program_manager->UseTrivialGeometryShader();
+ break;
+ }
continue;
}
@@ -288,11 +293,18 @@ void RasterizerOpenGL::SetupShaders() {
switch (program) {
case Maxwell::ShaderProgram::VertexA:
case Maxwell::ShaderProgram::VertexB: {
- shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle());
+ shader_program_manager->UseProgrammableVertexShader(
+ shader->GetProgramHandle(primitive_mode));
+ break;
+ }
+ case Maxwell::ShaderProgram::Geometry: {
+ shader_program_manager->UseProgrammableGeometryShader(
+ shader->GetProgramHandle(primitive_mode));
break;
}
case Maxwell::ShaderProgram::Fragment: {
- shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle());
+ shader_program_manager->UseProgrammableFragmentShader(
+ shader->GetProgramHandle(primitive_mode));
break;
}
default:
@@ -302,12 +314,13 @@ void RasterizerOpenGL::SetupShaders() {
}
// Configure the const buffers for this shader stage.
- current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage),
- shader, current_constbuffer_bindpoint);
+ current_constbuffer_bindpoint =
+ SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
+ current_constbuffer_bindpoint);
// Configure the textures for this shader stage.
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
- current_texture_bindpoint);
+ primitive_mode, current_texture_bindpoint);
// When VertexA is enabled, we have dual vertex shaders
if (program == Maxwell::ShaderProgram::VertexA) {
@@ -317,8 +330,6 @@ void RasterizerOpenGL::SetupShaders() {
}
state.Apply();
-
- shader_program_manager->UseTrivialGeometryShader();
}
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -541,6 +552,7 @@ void RasterizerOpenGL::DrawArrays() {
SyncLogicOpState();
SyncCullMode();
SyncAlphaTest();
+ SyncScissorTest();
SyncTransformFeedback();
SyncPointState();
@@ -580,7 +592,7 @@ void RasterizerOpenGL::DrawArrays() {
SetupVertexArrays();
DrawParameters params = SetupDraw();
- SetupShaders();
+ SetupShaders(params.primitive_mode);
buffer_cache.Unmap();
@@ -719,7 +731,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
}
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
- u32 current_bindpoint) {
+ GLenum primitive_mode, u32 current_bindpoint) {
MICROPROFILE_SCOPE(OpenGL_UBO);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
@@ -771,7 +783,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
// Now configure the bindpoint of the buffer inside the shader
- glUniformBlockBinding(shader->GetProgramHandle(),
+ glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
shader->GetProgramResourceIndex(used_buffer),
current_bindpoint + bindpoint);
@@ -787,7 +799,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
return current_bindpoint + static_cast<u32>(entries.size());
}
-u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) {
+u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
+ GLenum primitive_mode, u32 current_unit) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
@@ -802,8 +815,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
// Bind the uniform to the sampler.
- glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry),
- current_bindpoint);
+ glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
+ shader->GetUniformLocation(entry), current_bindpoint);
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
@@ -972,6 +985,22 @@ void RasterizerOpenGL::SyncAlphaTest() {
}
}
+void RasterizerOpenGL::SyncScissorTest() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+
+ state.scissor.enabled = (regs.scissor_test.enable != 0);
+ // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's
+ // implemented.
+ if (regs.scissor_test.enable != 0) {
+ const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
+ const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
+ state.scissor.x = regs.scissor_test.min_x;
+ state.scissor.y = regs.scissor_test.min_y;
+ state.scissor.width = width;
+ state.scissor.height = height;
+ }
+}
+
void RasterizerOpenGL::SyncTransformFeedback() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 0dab2018b..b1f7ccc7e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -120,7 +120,7 @@ private:
* @returns The next available bindpoint for use in the next shader stage.
*/
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
- u32 current_bindpoint);
+ GLenum primitive_mode, u32 current_bindpoint);
/*
* Configures the current textures to use for the draw command.
@@ -130,7 +130,7 @@ private:
* @returns The next available bindpoint for use in the next shader stage.
*/
u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
- u32 current_unit);
+ GLenum primitive_mode, u32 current_unit);
/// Syncs the viewport to match the guest state
void SyncViewport();
@@ -165,6 +165,9 @@ private:
/// Syncs the alpha test state to match the guest state
void SyncAlphaTest();
+ /// Syncs the scissor test state to match the guest state
+ void SyncScissorTest();
+
/// Syncs the transform feedback state to match the guest state
void SyncTransformFeedback();
@@ -207,7 +210,7 @@ private:
DrawParameters SetupDraw();
- void SetupShaders();
+ void SetupShaders(GLenum primitive_mode);
enum class AccelDraw { Disabled, Arrays, Indexed };
AccelDraw accelerate_draw = AccelDraw::Disabled;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 56ff83eff..65a220c41 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -45,7 +45,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
SurfaceParams params{};
params.addr = TryGetCpuAddr(config.tic.Address());
params.is_tiled = config.tic.IsTiled();
+ params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
+ params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
params.pixel_format =
PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
@@ -97,8 +99,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
SurfaceParams params{};
params.addr = TryGetCpuAddr(config.Address());
- params.is_tiled = true;
- params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
+ params.is_tiled =
+ config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
+ params.block_width = 1 << config.memory_layout.block_width;
+ params.block_height = 1 << config.memory_layout.block_height;
+ params.block_depth = 1 << config.memory_layout.block_depth;
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
params.component_type = ComponentTypeFromRenderTarget(config.format);
params.type = GetFormatType(params.pixel_format);
@@ -120,13 +125,16 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
return params;
}
-/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
- Tegra::GPUVAddr zeta_address,
- Tegra::DepthFormat format) {
+/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
+ u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
+ u32 block_width, u32 block_height, u32 block_depth,
+ Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
SurfaceParams params{};
params.addr = TryGetCpuAddr(zeta_address);
- params.is_tiled = true;
- params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
+ params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
+ params.block_width = 1 << std::min(block_width, 5U);
+ params.block_height = 1 << std::min(block_height, 5U);
+ params.block_depth = 1 << std::min(block_depth, 5U);
params.pixel_format = PixelFormatFromDepthFormat(format);
params.component_type = ComponentTypeFromDepthFormat(format);
params.type = GetFormatType(params.pixel_format);
@@ -148,7 +156,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
SurfaceParams params{};
params.addr = TryGetCpuAddr(config.Address());
params.is_tiled = !config.linear;
- params.block_height = params.is_tiled ? config.BlockHeight() : 0,
+ params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
+ params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
+ params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
params.component_type = ComponentTypeFromRenderTarget(config.format);
params.type = GetFormatType(params.pixel_format);
@@ -818,6 +828,11 @@ void CachedSurface::LoadGLBuffer() {
if (params.is_tiled) {
gl_buffer.resize(total_size);
+ ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
+ params.block_width, static_cast<u32>(params.target));
+ ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}",
+ params.block_depth, static_cast<u32>(params.target));
+
// TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
// this for 3D textures, etc.
switch (params.target) {
@@ -989,7 +1004,9 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
}
SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
- regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)};
+ regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format,
+ regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
+ regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
return GetSurface(depth_params, preserve_contents);
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0b4940b3c..66d98ad4e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -716,9 +716,10 @@ struct SurfaceParams {
static SurfaceParams CreateForFramebuffer(std::size_t index);
/// Creates SurfaceParams for a depth buffer configuration
- static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
- Tegra::GPUVAddr zeta_address,
- Tegra::DepthFormat format);
+ static SurfaceParams CreateForDepthBuffer(
+ u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
+ u32 block_width, u32 block_height, u32 block_depth,
+ Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
/// Creates SurfaceParams for a Fermi2D surface copy
static SurfaceParams CreateForFermiCopySurface(
@@ -733,7 +734,9 @@ struct SurfaceParams {
VAddr addr;
bool is_tiled;
+ u32 block_width;
u32 block_height;
+ u32 block_depth;
PixelFormat pixel_format;
ComponentType component_type;
SurfaceType type;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7cd8f91e4..1a03a677f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -68,6 +68,10 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
program_result = GLShader::GenerateVertexShader(setup);
gl_type = GL_VERTEX_SHADER;
break;
+ case Maxwell::ShaderProgram::Geometry:
+ program_result = GLShader::GenerateGeometryShader(setup);
+ gl_type = GL_GEOMETRY_SHADER;
+ break;
case Maxwell::ShaderProgram::Fragment:
program_result = GLShader::GenerateFragmentShader(setup);
gl_type = GL_FRAGMENT_SHADER;
@@ -80,11 +84,16 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
entries = program_result.second;
- OGLShader shader;
- shader.Create(program_result.first.c_str(), gl_type);
- program.Create(true, shader.handle);
- SetShaderUniformBlockBindings(program.handle);
- VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
+ if (program_type != Maxwell::ShaderProgram::Geometry) {
+ OGLShader shader;
+ shader.Create(program_result.first.c_str(), gl_type);
+ program.Create(true, shader.handle);
+ SetShaderUniformBlockBindings(program.handle);
+ VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
+ } else {
+ // Store shader's code to lazily build it on draw
+ geometry_programs.code = program_result.first;
+ }
}
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
@@ -110,6 +119,21 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
return search->second;
}
+GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
+ const std::string& glsl_topology,
+ const std::string& debug_name) {
+ if (target_program.handle != 0) {
+ return target_program.handle;
+ }
+ const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
+ OGLShader shader;
+ shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
+ target_program.Create(true, shader.handle);
+ SetShaderUniformBlockBindings(target_program.handle);
+ VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
+ return target_program.handle;
+};
+
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
const VAddr program_addr{GetShaderAddress(program)};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 9bafe43a9..7bb287f56 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -7,6 +7,7 @@
#include <map>
#include <memory>
+#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -38,8 +39,31 @@ public:
}
/// Gets the GL program handle for the shader
- GLuint GetProgramHandle() const {
- return program.handle;
+ GLuint GetProgramHandle(GLenum primitive_mode) {
+ if (program_type != Maxwell::ShaderProgram::Geometry) {
+ return program.handle;
+ }
+ switch (primitive_mode) {
+ case GL_POINTS:
+ return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
+ case GL_LINES:
+ case GL_LINE_STRIP:
+ return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
+ case GL_LINES_ADJACENCY:
+ case GL_LINE_STRIP_ADJACENCY:
+ return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
+ "ShaderLinesAdjacency");
+ case GL_TRIANGLES:
+ case GL_TRIANGLE_STRIP:
+ case GL_TRIANGLE_FAN:
+ return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
+ case GL_TRIANGLES_ADJACENCY:
+ case GL_TRIANGLE_STRIP_ADJACENCY:
+ return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
+ "ShaderLines");
+ default:
+ UNREACHABLE_MSG("Unknown primitive mode.");
+ }
}
/// Gets the GL program resource location for the specified resource, caching as needed
@@ -49,12 +73,30 @@ public:
GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
private:
+ /// Generates a geometry shader or returns one that already exists.
+ GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
+ const std::string& debug_name);
+
VAddr addr;
Maxwell::ShaderProgram program_type;
GLShader::ShaderSetup setup;
GLShader::ShaderEntries entries;
+
+ // Non-geometry program.
OGLProgram program;
+ // Geometry programs. These are needed because GLSL needs an input topology but it's not
+ // declared by the hardware. Workaround this issue by generating a different shader per input
+ // topology class.
+ struct {
+ std::string code;
+ OGLProgram points;
+ OGLProgram lines;
+ OGLProgram lines_adjacency;
+ OGLProgram triangles;
+ OGLProgram triangles_adjacency;
+ } geometry_programs;
+
std::map<u32, GLuint> resource_cache;
std::map<u32, GLint> uniform_cache;
};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 7e57de78a..8dfb49507 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -7,6 +7,7 @@
#include <string>
#include <string_view>
+#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/assert.h"
@@ -29,11 +30,32 @@ using Tegra::Shader::SubOp;
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
+enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
+
+constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
+constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
+
class DecompileFail : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
+/// Translate topology
+static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
+ switch (topology) {
+ case Tegra::Shader::OutputTopology::PointList:
+ return "points";
+ case Tegra::Shader::OutputTopology::LineStrip:
+ return "line_strip";
+ case Tegra::Shader::OutputTopology::TriangleStrip:
+ return "triangle_strip";
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology));
+ UNREACHABLE();
+ return "points";
+ }
+}
+
/// Describes the behaviour of code path of a given entry point and a return point.
enum class ExitMethod {
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
@@ -253,8 +275,9 @@ enum class InternalFlag : u64 {
class GLSLRegisterManager {
public:
GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
- const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix)
- : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} {
+ const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
+ const Tegra::Shader::Header& header)
+ : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} {
BuildRegisterList();
BuildInputList();
}
@@ -358,11 +381,13 @@ public:
* @param reg The destination register to use.
* @param elem The element to use for the operation.
* @param attribute The input attribute to use as the source value.
+ * @param vertex The register that decides which vertex to read from (used in GS).
*/
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
- const Tegra::Shader::IpaMode& input_mode) {
+ const Tegra::Shader::IpaMode& input_mode,
+ boost::optional<Register> vertex = {}) {
const std::string dest = GetRegisterAsFloat(reg);
- const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem);
+ const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
shader.AddLine(dest + " = " + src + ';');
}
@@ -391,16 +416,29 @@ public:
* are stored as floats, so this may require conversion.
* @param attribute The destination output attribute.
* @param elem The element to use for the operation.
- * @param reg The register to use as the source value.
+ * @param val_reg The register to use as the source value.
+ * @param buf_reg The register that tells which buffer to write to (used in geometry shaders).
*/
- void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) {
+ void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg,
+ const Register& buf_reg) {
const std::string dest = GetOutputAttribute(attribute);
- const std::string src = GetRegisterAsFloat(reg);
+ const std::string src = GetRegisterAsFloat(val_reg);
if (!dest.empty()) {
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
// instruction for now.
- shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
+ if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
+ // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
+ // shader. These instructions use a dirty register as buffer index. To avoid some
+ // drivers from complaining for the out of boundary writes, guard them.
+ const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
+ std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
+ shader.AddLine("amem[" + buf_index + "][" +
+ std::to_string(static_cast<u32>(attribute)) + ']' +
+ GetSwizzle(elem) + " = " + src + ';');
+ } else {
+ shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
+ }
}
}
@@ -441,41 +479,123 @@ public:
}
}
- /// Add declarations for registers
+ /// Add declarations.
void GenerateDeclarations(const std::string& suffix) {
+ GenerateRegisters(suffix);
+ GenerateInternalFlags();
+ GenerateInputAttrs();
+ GenerateOutputAttrs();
+ GenerateConstBuffers();
+ GenerateSamplers();
+ GenerateGeometry();
+ }
+
+ /// Returns a list of constant buffer declarations.
+ std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
+ std::vector<ConstBufferEntry> result;
+ std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
+ std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
+ return result;
+ }
+
+ /// Returns a list of samplers used in the shader.
+ const std::vector<SamplerEntry>& GetSamplers() const {
+ return used_samplers;
+ }
+
+ /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
+ /// necessary.
+ std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
+ bool is_array, bool is_shadow) {
+ const auto offset = static_cast<std::size_t>(sampler.index.Value());
+
+ // If this sampler has already been used, return the existing mapping.
+ const auto itr =
+ std::find_if(used_samplers.begin(), used_samplers.end(),
+ [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
+
+ if (itr != used_samplers.end()) {
+ ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
+ itr->IsShadow() == is_shadow);
+ return itr->GetName();
+ }
+
+ // Otherwise create a new mapping for this sampler
+ const std::size_t next_index = used_samplers.size();
+ const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow};
+ used_samplers.emplace_back(entry);
+ return entry.GetName();
+ }
+
+private:
+ /// Generates declarations for registers.
+ void GenerateRegisters(const std::string& suffix) {
for (const auto& reg : regs) {
declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
}
declarations.AddNewLine();
+ }
+ /// Generates declarations for internal flags.
+ void GenerateInternalFlags() {
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
const InternalFlag code = static_cast<InternalFlag>(ii);
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
}
declarations.AddNewLine();
+ }
+
+ /// Generates declarations for input attributes.
+ void GenerateInputAttrs() {
+ if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
+ const std::string attr =
+ stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position";
+ declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
+ ") in vec4 " + attr + ';');
+ }
for (const auto element : declr_input_attribute) {
// TODO(bunnei): Use proper number of elements for these
u32 idx =
static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
- declarations.AddLine("layout(location = " + std::to_string(idx) + ")" +
- GetInputFlags(element.first) + "in vec4 " +
- GetInputAttribute(element.first, element.second) + ';');
+ if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
+ // If inputs are varyings, add an offset
+ idx += GENERIC_VARYING_START_LOCATION;
+ }
+
+ std::string attr{GetInputAttribute(element.first, element.second)};
+ if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
+ attr = "gs_" + attr + "[]";
+ }
+ declarations.AddLine("layout (location = " + std::to_string(idx) + ") " +
+ GetInputFlags(element.first) + "in vec4 " + attr + ';');
}
+
declarations.AddNewLine();
+ }
+ /// Generates declarations for output attributes.
+ void GenerateOutputAttrs() {
+ if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
+ declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
+ ") out vec4 position;");
+ }
for (const auto& index : declr_output_attribute) {
// TODO(bunnei): Use proper number of elements for these
- declarations.AddLine("layout(location = " +
- std::to_string(static_cast<u32>(index) -
- static_cast<u32>(Attribute::Index::Attribute_0)) +
- ") out vec4 " + GetOutputAttribute(index) + ';');
+ const u32 idx = static_cast<u32>(index) -
+ static_cast<u32>(Attribute::Index::Attribute_0) +
+ GENERIC_VARYING_START_LOCATION;
+ declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
+ GetOutputAttribute(index) + ';');
}
declarations.AddNewLine();
+ }
+ /// Generates declarations for constant buffers.
+ void GenerateConstBuffers() {
for (const auto& entry : GetConstBuffersDeclarations()) {
- declarations.AddLine("layout(std140) uniform " + entry.GetName());
+ declarations.AddLine("layout (std140) uniform " + entry.GetName());
declarations.AddLine('{');
declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
"[MAX_CONSTBUFFER_ELEMENTS];");
@@ -483,7 +603,10 @@ public:
declarations.AddNewLine();
}
declarations.AddNewLine();
+ }
+ /// Generates declarations for samplers.
+ void GenerateSamplers() {
const auto& samplers = GetSamplers();
for (const auto& sampler : samplers) {
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
@@ -492,44 +615,42 @@ public:
declarations.AddNewLine();
}
- /// Returns a list of constant buffer declarations
- std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
- std::vector<ConstBufferEntry> result;
- std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
- std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
- return result;
- }
-
- /// Returns a list of samplers used in the shader
- const std::vector<SamplerEntry>& GetSamplers() const {
- return used_samplers;
- }
-
- /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
- /// necessary.
- std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
- bool is_array, bool is_shadow) {
- const std::size_t offset = static_cast<std::size_t>(sampler.index.Value());
+ /// Generates declarations used for geometry shaders.
+ void GenerateGeometry() {
+ if (stage != Maxwell3D::Regs::ShaderStage::Geometry)
+ return;
- // If this sampler has already been used, return the existing mapping.
- const auto itr =
- std::find_if(used_samplers.begin(), used_samplers.end(),
- [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
+ declarations.AddLine(
+ "layout (" + GetTopologyName(header.common3.output_topology) +
+ ", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;");
+ declarations.AddNewLine();
- if (itr != used_samplers.end()) {
- ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
- itr->IsShadow() == is_shadow);
- return itr->GetName();
- }
+ declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" +
+ std::to_string(MAX_ATTRIBUTES) + "];");
+ declarations.AddNewLine();
- // Otherwise create a new mapping for this sampler
- const std::size_t next_index = used_samplers.size();
- const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow};
- used_samplers.emplace_back(entry);
- return entry.GetName();
+ constexpr char buffer[] = "amem[output_buffer]";
+ declarations.AddLine("void emit_vertex(uint output_buffer) {");
+ ++declarations.scope;
+ for (const auto element : declr_output_attribute) {
+ declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' +
+ std::to_string(static_cast<u32>(element)) + "];");
+ }
+
+ declarations.AddLine("position = " + std::string(buffer) + '[' +
+ std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];");
+
+ // If a geometry shader is attached, it will always flip (it's the last stage before
+ // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
+ declarations.AddLine("position.xy *= viewport_flip.xy;");
+ declarations.AddLine("gl_Position = position;");
+ declarations.AddLine("position.w = 1.0;");
+ declarations.AddLine("EmitVertex();");
+ --declarations.scope;
+ declarations.AddLine('}');
+ declarations.AddNewLine();
}
-private:
/// Generates code representing a temporary (GPR) register.
std::string GetRegister(const Register& reg, unsigned elem) {
if (reg == Register::ZeroIndex) {
@@ -586,11 +707,19 @@ private:
/// Generates code representing an input attribute register.
std::string GetInputAttribute(Attribute::Index attribute,
- const Tegra::Shader::IpaMode& input_mode) {
+ const Tegra::Shader::IpaMode& input_mode,
+ boost::optional<Register> vertex = {}) {
+ auto GeometryPass = [&](const std::string& name) {
+ if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
+ return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']';
+ }
+ return name;
+ };
+
switch (attribute) {
case Attribute::Index::Position:
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
- return "position";
+ return GeometryPass("position");
} else {
return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
}
@@ -619,7 +748,7 @@ private:
UNREACHABLE();
}
}
- return "input_attribute_" + std::to_string(index);
+ return GeometryPass("input_attribute_" + std::to_string(index));
}
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute));
@@ -672,7 +801,7 @@ private:
return out;
}
- /// Generates code representing an output attribute register.
+ /// Generates code representing the declaration name of an output attribute register.
std::string GetOutputAttribute(Attribute::Index attribute) {
switch (attribute) {
case Attribute::Index::Position:
@@ -708,6 +837,7 @@ private:
std::vector<SamplerEntry> used_samplers;
const Maxwell3D::Regs::ShaderStage& stage;
const std::string& suffix;
+ const Tegra::Shader::Header& header;
};
class GLSLGenerator {
@@ -1103,8 +1233,8 @@ private:
return offset + 1;
}
- shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" +
- std::to_string(instr.value) + ')');
+ shader.AddLine(
+ fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value));
using Tegra::Shader::Pred;
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1826,7 +1956,7 @@ private:
const auto LoadNextElement = [&](u32 reg_offset) {
regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
static_cast<Attribute::Index>(next_index),
- input_mode);
+ input_mode, instr.gpr39.Value());
// Load the next attribute element into the following register. If the element
// to load goes beyond the vec4 size, load the first element of the next
@@ -1890,8 +2020,8 @@ private:
const auto StoreNextElement = [&](u32 reg_offset) {
regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
- next_element,
- instr.gpr0.Value() + reg_offset);
+ next_element, instr.gpr0.Value() + reg_offset,
+ instr.gpr39.Value());
// Load the next attribute element into the following register. If the element
// to load goes beyond the vec4 size, load the first element of the next
@@ -2299,8 +2429,7 @@ private:
ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
"NDV is not implemented");
- const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
- const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const bool is_array = instr.tmml.array != 0;
auto texture_type = instr.tmml.texture_type.Value();
const std::string sampler =
@@ -2311,13 +2440,11 @@ private:
std::string coord;
switch (texture_type) {
case Tegra::Shader::TextureType::Texture1D: {
- std::string x = regs.GetRegisterAsFloat(instr.gpr8);
coord = "float coords = " + x + ';';
break;
}
case Tegra::Shader::TextureType::Texture2D: {
- std::string x = regs.GetRegisterAsFloat(instr.gpr8);
- std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
break;
}
@@ -2327,8 +2454,7 @@ private:
UNREACHABLE();
// Fallback to interpreting as a 2D texture for now
- std::string x = regs.GetRegisterAsFloat(instr.gpr8);
- std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
texture_type = Tegra::Shader::TextureType::Texture2D;
}
@@ -2738,6 +2864,52 @@ private:
break;
}
+ case OpCode::Id::OUT_R: {
+ ASSERT(instr.gpr20.Value() == Register::ZeroIndex);
+ ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
+ "OUT is expected to be used in a geometry shader.");
+
+ if (instr.out.emit) {
+ // gpr0 is used to store the next address. Hardware returns a pointer but
+ // we just return the next index with a cyclic cap.
+ const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)};
+ const std::string next = "((" + current + " + 1" + ") % " +
+ std::to_string(MAX_GEOMETRY_BUFFERS) + ')';
+ shader.AddLine("emit_vertex(" + current + ");");
+ regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1);
+ }
+ if (instr.out.cut) {
+ shader.AddLine("EndPrimitive();");
+ }
+
+ break;
+ }
+ case OpCode::Id::MOV_SYS: {
+ switch (instr.sys20) {
+ case Tegra::Shader::SystemVariable::InvocationInfo: {
+ LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
+ regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
+ break;
+ }
+ default: {
+ LOG_CRITICAL(HW_GPU, "Unhandled system move: {}",
+ static_cast<u32>(instr.sys20.Value()));
+ UNREACHABLE();
+ }
+ }
+ break;
+ }
+ case OpCode::Id::ISBERD: {
+ ASSERT(instr.isberd.o == 0);
+ ASSERT(instr.isberd.skew == 0);
+ ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None);
+ ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None);
+ ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
+ "ISBERD is expected to be used in a geometry shader.");
+ LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
+ regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1);
+ break;
+ }
case OpCode::Id::BRA: {
ASSERT_MSG(instr.bra.constant_buffer == 0,
"BRA with constant buffers are not implemented");
@@ -2781,6 +2953,88 @@ private:
LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
break;
}
+ case OpCode::Id::VMAD: {
+ const bool signed_a = instr.vmad.signed_a == 1;
+ const bool signed_b = instr.vmad.signed_b == 1;
+ const bool result_signed = signed_a || signed_b;
+ boost::optional<std::string> forced_result;
+
+ auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
+ Tegra::Shader::VmadType type, u64 byte_height) {
+ const std::string value = [&]() {
+ if (!is_chunk) {
+ const auto offset = static_cast<u32>(byte_height * 8);
+ return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
+ }
+ const std::string zero = "0";
+
+ switch (type) {
+ case Tegra::Shader::VmadType::Size16_Low:
+ return '(' + op + " & 0xffff)";
+ case Tegra::Shader::VmadType::Size16_High:
+ return '(' + op + " >> 16)";
+ case Tegra::Shader::VmadType::Size32:
+ // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
+ // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
+ // explanation is found: assert.
+ UNREACHABLE_MSG("Unimplemented");
+ return zero;
+ case Tegra::Shader::VmadType::Invalid:
+ // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
+ // testing (even though it's invalid) this makes the whole instruction
+ // assign zero to target register.
+ forced_result = boost::make_optional(zero);
+ return zero;
+ default:
+ UNREACHABLE();
+ return zero;
+ }
+ }();
+
+ if (is_signed) {
+ return "int(" + value + ')';
+ }
+ return value;
+ };
+
+ const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
+ instr.vmad.is_byte_chunk_a != 0, signed_a,
+ instr.vmad.type_a, instr.vmad.byte_height_a);
+
+ std::string op_b;
+ if (instr.vmad.use_register_b) {
+ op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
+ instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
+ instr.vmad.byte_height_b);
+ } else {
+ op_b = '(' +
+ std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
+ : instr.alu.GetImm20_16()) +
+ ')';
+ }
+
+ const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
+
+ std::string result;
+ if (forced_result) {
+ result = *forced_result;
+ } else {
+ result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
+
+ switch (instr.vmad.shr) {
+ case Tegra::Shader::VmadShr::Shr7:
+ result = '(' + result + " >> 7)";
+ break;
+ case Tegra::Shader::VmadShr::Shr15:
+ result = '(' + result + " >> 15)";
+ break;
+ }
+ }
+ regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
+ instr.vmad.saturate == 1, 0, Register::Size::Word,
+ instr.vmad.cc);
+ break;
+ }
default: {
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
UNREACHABLE();
@@ -2911,7 +3165,7 @@ private:
ShaderWriter shader;
ShaderWriter declarations;
- GLSLRegisterManager regs{shader, declarations, stage, suffix};
+ GLSLRegisterManager regs{shader, declarations, stage, suffix, header};
// Declarations
std::set<std::string> declr_predicates;
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index b0466c18f..1e5eb32df 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -17,7 +17,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
- out += "bool exec_vertex();\n";
+
+ out += R"(
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+
+layout(std140) uniform vs_config {
+ vec4 viewport_flip;
+ uvec4 instance_id;
+ uvec4 flip_stage;
+};
+)";
if (setup.IsDualProgram()) {
out += "bool exec_vertex_b();\n";
@@ -28,18 +39,17 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
.get_value_or({});
- out += R"(
-
-out gl_PerVertex {
- vec4 gl_Position;
-};
+ out += program.first;
-out vec4 position;
+ if (setup.IsDualProgram()) {
+ ProgramResult program_b =
+ Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
+ Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
+ .get_value_or({});
+ out += program_b.first;
+ }
-layout (std140) uniform vs_config {
- vec4 viewport_flip;
- uvec4 instance_id;
-};
+ out += R"(
void main() {
position = vec4(0.0, 0.0, 0.0, 0.0);
@@ -52,27 +62,52 @@ void main() {
out += R"(
- // Viewport can be flipped, which is unsupported by glViewport
- position.xy *= viewport_flip.xy;
+ // Check if the flip stage is VertexB
+ if (flip_stage[0] == 1) {
+ // Viewport can be flipped, which is unsupported by glViewport
+ position.xy *= viewport_flip.xy;
+ }
gl_Position = position;
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
// For now, this is here to bring order in lieu of proper emulation
- position.w = 1.0;
+ if (flip_stage[0] == 1) {
+ position.w = 1.0;
+ }
}
)";
- out += program.first;
+ return {out, program.second};
+}
- if (setup.IsDualProgram()) {
- ProgramResult program_b =
- Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
- Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
- .get_value_or({});
- out += program_b.first;
- }
+ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
+ std::string out = "#version 430 core\n";
+ out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
+ out += Decompiler::GetCommonDeclarations();
+ out += "bool exec_geometry();\n";
+
+ ProgramResult program =
+ Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
+ Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
+ .get_value_or({});
+ out += R"(
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+layout (std140) uniform gs_config {
+ vec4 viewport_flip;
+ uvec4 instance_id;
+ uvec4 flip_stage;
+};
+
+void main() {
+ exec_geometry();
+}
+
+)";
+ out += program.first;
return {out, program.second};
}
@@ -87,7 +122,6 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
.get_value_or({});
out += R"(
-in vec4 position;
layout(location = 0) out vec4 FragColor0;
layout(location = 1) out vec4 FragColor1;
layout(location = 2) out vec4 FragColor2;
@@ -100,6 +134,7 @@ layout(location = 7) out vec4 FragColor7;
layout (std140) uniform fs_config {
vec4 viewport_flip;
uvec4 instance_id;
+ uvec4 flip_stage;
};
void main() {
@@ -110,5 +145,4 @@ void main() {
out += program.first;
return {out, program.second};
}
-
-} // namespace OpenGL::GLShader
+} // namespace OpenGL::GLShader \ No newline at end of file
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index e56f39e78..79596087a 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -196,6 +196,12 @@ private:
ProgramResult GenerateVertexShader(const ShaderSetup& setup);
/**
+ * Generates the GLSL geometry shader program source code for the given GS program
+ * @returns String of the shader source code
+ */
+ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
+
+/**
* Generates the GLSL fragment shader program source code for the given FS program
* @returns String of the shader source code
*/
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 022d32a86..010857ec6 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -18,6 +18,14 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
// We only assign the instance to the first component of the vector, the rest is just padding.
instance_id[0] = state.current_instance;
+
+ // Assign in which stage the position has to be flipped
+ // (the last stage before the fragment shader).
+ if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
+ flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
+ } else {
+ flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
+ }
}
} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 3de15ba9b..b3a191cf2 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,8 +21,9 @@ struct MaxwellUniformData {
void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
alignas(16) GLvec4 viewport_flip;
alignas(16) GLuvec4 instance_id;
+ alignas(16) GLuvec4 flip_stage;
};
-static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect");
+static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
static_assert(sizeof(MaxwellUniformData) < 16384,
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
@@ -36,6 +37,10 @@ public:
vs = program;
}
+ void UseProgrammableGeometryShader(GLuint program) {
+ gs = program;
+ }
+
void UseProgrammableFragmentShader(GLuint program) {
fs = program;
}
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3d5476e5d..0d2456b56 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -4,6 +4,7 @@
#include <cmath>
#include <cstring>
+#include "common/alignment.h"
#include "common/assert.h"
#include "core/memory.h"
#include "video_core/gpu.h"
@@ -199,4 +200,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
return rgba_data;
}
+std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
+ u32 block_height, u32 block_depth) {
+ if (tiled) {
+ const u32 gobs_in_x = 64 / bytes_per_pixel;
+ const u32 gobs_in_y = 8;
+ const u32 gobs_in_z = 1;
+ const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
+ const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
+ const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
+ return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
+ } else {
+ return width * height * depth * bytes_per_pixel;
+ }
+}
+
} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 1f7b731be..234d250af 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -32,4 +32,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
u32 height);
+/**
+ * This function calculates the correct size of a texture depending if it's tiled or not.
+ */
+std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
+ u32 block_height, u32 block_depth);
+
} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 8f31d825a..58d17abcb 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -161,7 +161,9 @@ struct TICEntry {
BitField<21, 3, TICHeaderVersion> header_version;
};
union {
+ BitField<0, 3, u32> block_width;
BitField<3, 3, u32> block_height;
+ BitField<6, 3, u32> block_depth;
// High 16 bits of the pitch value
BitField<0, 16, u32> pitch_high;
@@ -202,13 +204,24 @@ struct TICEntry {
return depth_minus_1 + 1;
}
+ u32 BlockWidth() const {
+ ASSERT(IsTiled());
+ // The block height is stored in log2 format.
+ return 1 << block_width;
+ }
+
u32 BlockHeight() const {
- ASSERT(header_version == TICHeaderVersion::BlockLinear ||
- header_version == TICHeaderVersion::BlockLinearColorKey);
+ ASSERT(IsTiled());
// The block height is stored in log2 format.
return 1 << block_height;
}
+ u32 BlockDepth() const {
+ ASSERT(IsTiled());
+ // The block height is stored in log2 format.
+ return 1 << block_depth;
+ }
+
bool IsTiled() const {
return header_version == TICHeaderVersion::BlockLinear ||
header_version == TICHeaderVersion::BlockLinearColorKey;
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 681919ae3..237cc1307 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -169,16 +169,20 @@ static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
const std::string nice_addr = fmt::format("0x{:016x}", addr);
std::string object_label;
- switch (identifier) {
- case GL_TEXTURE:
- object_label = extra_info + "@" + nice_addr;
- break;
- case GL_PROGRAM:
- object_label = "ShaderProgram@" + nice_addr;
- break;
- default:
- object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
- break;
+ if (extra_info.empty()) {
+ switch (identifier) {
+ case GL_TEXTURE:
+ object_label = "Texture@" + nice_addr;
+ break;
+ case GL_PROGRAM:
+ object_label = "Shader@" + nice_addr;
+ break;
+ default:
+ object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
+ break;
+ }
+ } else {
+ object_label = extra_info + '@' + nice_addr;
}
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
}