summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/engines/shader_bytecode.h4
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/macro_interpreter.cpp2
-rw-r--r--src/video_core/morton.cpp1
-rw-r--r--src/video_core/renderer_base.cpp12
-rw-r--r--src/video_core/renderer_base.h27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp279
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp43
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h9
-rw-r--r--src/video_core/surface.cpp7
-rw-r--r--src/video_core/textures/decoders.cpp2
-rw-r--r--src/video_core/video_core.cpp8
-rw-r--r--src/video_core/video_core.h2
16 files changed, 274 insertions, 139 deletions
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 25bb7604a..0faff6fdf 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -164,6 +164,7 @@ public:
return 3;
default:
UNREACHABLE();
+ return 1;
}
}
@@ -871,6 +872,7 @@ public:
return 4;
}
UNREACHABLE();
+ return 1;
}
GPUVAddr StartAddress() const {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 2efeb6e1a..e53c77f2b 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -1049,7 +1049,7 @@ union Instruction {
BitField<49, 1, u64> nodep_flag;
BitField<50, 3, u64> component_mask_selector;
BitField<53, 4, u64> texture_info;
- BitField<60, 1, u64> fp32_flag;
+ BitField<59, 1, u64> fp32_flag;
TextureType GetTextureType() const {
// The TEXS instruction has a weird encoding for the texture type.
@@ -1065,6 +1065,7 @@ union Instruction {
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
static_cast<u32>(texture_info.Value()));
UNREACHABLE();
+ return TextureType::Texture1D;
}
TextureProcessMode GetTextureProcessMode() const {
@@ -1145,6 +1146,7 @@ union Instruction {
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
static_cast<u32>(texture_info.Value()));
UNREACHABLE();
+ return TextureType::Texture1D;
}
TextureProcessMode GetTextureProcessMode() const {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 88c45a423..08cf6268f 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -102,6 +102,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
return 1;
default:
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
+ return 1;
}
}
@@ -119,6 +120,7 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
return 2;
default:
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
+ return 1;
}
}
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 9c55e9f1e..64f75db43 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -171,6 +171,7 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b)
default:
UNIMPLEMENTED_MSG("Unimplemented ALU operation {}", static_cast<u32>(operation));
+ return 0;
}
}
@@ -268,6 +269,7 @@ bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value)
return value != 0;
}
UNREACHABLE();
+ return true;
}
} // namespace Tegra
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index a310491a8..47e76d8fe 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -192,6 +192,7 @@ static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFor
return linear_to_morton_fns[static_cast<std::size_t>(format)];
}
UNREACHABLE();
+ return morton_to_linear_fns[static_cast<std::size_t>(format)];
}
/// 8x8 Z-Order coordinate from 2D coordinates
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 1482cdb40..94223f45f 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
}
+void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
+ const Layout::FramebufferLayout& layout) {
+ if (renderer_settings.screenshot_requested) {
+ LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
+ return;
+ }
+ renderer_settings.screenshot_bits = data;
+ renderer_settings.screenshot_complete_callback = std::move(callback);
+ renderer_settings.screenshot_framebuffer_layout = layout;
+ renderer_settings.screenshot_requested = true;
+}
+
} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 669e26e15..1d54c3723 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -9,6 +9,7 @@
#include <optional>
#include "common/common_types.h"
+#include "core/frontend/emu_window.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
@@ -21,6 +22,12 @@ namespace VideoCore {
struct RendererSettings {
std::atomic_bool use_framelimiter{false};
std::atomic_bool set_background_color{false};
+
+ // Screenshot
+ std::atomic<bool> screenshot_requested{false};
+ void* screenshot_bits;
+ std::function<void()> screenshot_complete_callback;
+ Layout::FramebufferLayout screenshot_framebuffer_layout;
};
class RendererBase : NonCopyable {
@@ -57,9 +64,29 @@ public:
return *rasterizer;
}
+ Core::Frontend::EmuWindow& GetRenderWindow() {
+ return render_window;
+ }
+
+ const Core::Frontend::EmuWindow& GetRenderWindow() const {
+ return render_window;
+ }
+
+ RendererSettings& Settings() {
+ return renderer_settings;
+ }
+
+ const RendererSettings& Settings() const {
+ return renderer_settings;
+ }
+
/// Refreshes the settings common to all renderers
void RefreshBaseSettings();
+ /// Request a screenshot of the next frame
+ void RequestScreenshot(void* data, std::function<void()> callback,
+ const Layout::FramebufferLayout& layout);
+
protected:
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
std::unique_ptr<RasterizerInterface> rasterizer;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 5f4cdd119..7ea07631a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -101,8 +101,18 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
params.srgb_conversion);
+
+ if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) {
+ // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled,
+ // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also
+ // causes GetFormatType to properly return 'Depth' below).
+ params.pixel_format = PixelFormat::Z16;
+ }
+
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
+ UNIMPLEMENTED_IF(params.type == SurfaceType::ColorTexture && config.tsc.depth_compare_enabled);
+
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.unaligned_height = config.tic.Height();
@@ -257,7 +267,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U
- {GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI
+ {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
false}, // R11FG11FB10F
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index b4ef6030d..de3671acf 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -67,6 +67,7 @@ public:
6, "ShaderTrianglesAdjacency");
default:
UNREACHABLE_MSG("Unknown primitive mode.");
+ return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
}
}
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index bd61af463..a1cef99ae 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -347,6 +347,15 @@ public:
BuildInputList();
}
+ void SetConditionalCodesFromExpression(const std::string& expresion) {
+ SetInternalFlag(InternalFlag::ZeroFlag, "(" + expresion + ") == 0");
+ LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
+ }
+
+ void SetConditionalCodesFromRegister(const Register& reg, u64 dest_elem = 0) {
+ SetConditionalCodesFromExpression(GetRegister(reg, static_cast<u32>(dest_elem)));
+ }
+
/**
* Returns code that does an integer size conversion for the specified size.
* @param value Value to perform integer size conversion on.
@@ -364,6 +373,7 @@ public:
return value;
default:
UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
+ return value;
}
}
@@ -400,14 +410,24 @@ public:
* @param dest_num_components Number of components in the destination.
* @param value_num_components Number of components in the value.
* @param is_saturated Optional, when True, saturates the provided value.
+ * @param sets_cc Optional, when True, sets the corresponding values to the implemented
+ * condition flags.
* @param dest_elem Optional, the destination element to use for the operation.
*/
void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
u64 dest_num_components, u64 value_num_components,
- bool is_saturated = false, u64 dest_elem = 0, bool precise = false) {
-
- SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
- dest_num_components, value_num_components, dest_elem, precise);
+ bool is_saturated = false, bool sets_cc = false, u64 dest_elem = 0,
+ bool precise = false) {
+ const std::string clamped_value = is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value;
+ SetRegister(reg, elem, clamped_value, dest_num_components, value_num_components, dest_elem,
+ precise);
+ if (sets_cc) {
+ if (reg == Register::ZeroIndex) {
+ SetConditionalCodesFromExpression(clamped_value);
+ } else {
+ SetConditionalCodesFromRegister(reg, dest_elem);
+ }
+ }
}
/**
@@ -418,25 +438,29 @@ public:
* @param dest_num_components Number of components in the destination.
* @param value_num_components Number of components in the value.
* @param is_saturated Optional, when True, saturates the provided value.
+ * @param sets_cc Optional, when True, sets the corresponding values to the implemented
+ * condition flags.
* @param dest_elem Optional, the destination element to use for the operation.
* @param size Register size to use for conversion instructions.
*/
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
const std::string& value, u64 dest_num_components,
u64 value_num_components, bool is_saturated = false,
- u64 dest_elem = 0, Register::Size size = Register::Size::Word,
- bool sets_cc = false) {
+ bool sets_cc = false, u64 dest_elem = 0,
+ Register::Size size = Register::Size::Word) {
UNIMPLEMENTED_IF(is_saturated);
-
+ const std::string final_value = ConvertIntegerSize(value, size);
const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
- SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
- dest_num_components, value_num_components, dest_elem, false);
+ SetRegister(reg, elem, func + '(' + final_value + ')', dest_num_components,
+ value_num_components, dest_elem, false);
if (sets_cc) {
- const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
- SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
- LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
+ if (reg == Register::ZeroIndex) {
+ SetConditionalCodesFromExpression(final_value);
+ } else {
+ SetConditionalCodesFromRegister(reg, dest_elem);
+ }
}
}
@@ -626,6 +650,7 @@ public:
return "floatBitsToInt(" + value + ')';
} else {
UNREACHABLE();
+ return value;
}
}
@@ -1273,7 +1298,7 @@ private:
void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
const std::string& op_b,
Tegra::Shader::PredicateResultMode predicate_mode,
- Tegra::Shader::Pred predicate) {
+ Tegra::Shader::Pred predicate, const bool set_cc) {
std::string result{};
switch (logic_op) {
case LogicOperation::And: {
@@ -1297,7 +1322,7 @@ private:
}
if (dest != Tegra::Shader::Register::ZeroIndex) {
- regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
+ regs.SetRegisterToInteger(dest, true, 0, result, 1, 1, false, set_cc);
}
using Tegra::Shader::PredicateResultMode;
@@ -1317,7 +1342,8 @@ private:
}
void WriteLop3Instruction(Register dest, const std::string& op_a, const std::string& op_b,
- const std::string& op_c, const std::string& imm_lut) {
+ const std::string& op_c, const std::string& imm_lut,
+ const bool set_cc) {
if (dest == Tegra::Shader::Register::ZeroIndex) {
return;
}
@@ -1340,7 +1366,7 @@ private:
result += ')';
- regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
+ regs.SetRegisterToInteger(dest, true, 0, result, 1, 1, false, set_cc);
}
void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) {
@@ -1355,12 +1381,12 @@ private:
if (written_components < 2) {
// Write the first two swizzle components to gpr0 and gpr0+1
- regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false,
+ regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, false,
written_components % 2);
} else {
ASSERT(instr.texs.HasTwoDestinations());
// Write the rest of the swizzle components to gpr28 and gpr28+1
- regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false,
+ regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, false,
written_components % 2);
}
@@ -1591,23 +1617,21 @@ private:
process_mode == Tegra::Shader::TextureProcessMode::LL ||
process_mode == Tegra::Shader::TextureProcessMode::LLA;
+ // LOD selection (either via bias or explicit textureLod) not supported in GL for
+ // sampler2DArrayShadow and samplerCubeArrayShadow.
const bool gl_lod_supported = !(
(texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
- (texture_type == Tegra::Shader::TextureType::TextureCube && !is_array &&
- depth_compare));
+ (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && depth_compare));
const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture(";
std::string texture = read_method + sampler + ", coord";
- if (process_mode != Tegra::Shader::TextureProcessMode::None) {
+ UNIMPLEMENTED_IF(process_mode != Tegra::Shader::TextureProcessMode::None &&
+ !gl_lod_supported);
+
+ if (process_mode != Tegra::Shader::TextureProcessMode::None && gl_lod_supported) {
if (process_mode == Tegra::Shader::TextureProcessMode::LZ) {
- if (gl_lod_supported) {
- texture += ", 0";
- } else {
- // Lod 0 is emulated by a big negative bias
- // in scenarios that are not supported by glsl
- texture += ", -1000";
- }
+ texture += ", 0.0";
} else {
// If present, lod or bias are always stored in the register indexed by the
// gpr20
@@ -1645,15 +1669,15 @@ private:
if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) {
coord += ",0.0";
}
+ if (is_array) {
+ coord += ',' + regs.GetRegisterAsInteger(array_register);
+ }
if (depth_compare) {
// Depth is always stored in the register signaled by gpr20
// or in the next register if lod or bias are used
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
coord += ',' + regs.GetRegisterAsFloat(depth_register);
}
- if (is_array) {
- coord += ',' + regs.GetRegisterAsInteger(array_register);
- }
coord += ");";
return std::make_pair(
coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0));
@@ -1686,15 +1710,15 @@ private:
}
}
+ if (is_array) {
+ coord += ',' + regs.GetRegisterAsInteger(array_register);
+ }
if (depth_compare) {
// Depth is always stored in the register signaled by gpr20
// or in the next register if lod or bias are used
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
coord += ',' + regs.GetRegisterAsFloat(depth_register);
}
- if (is_array) {
- coord += ',' + regs.GetRegisterAsInteger(array_register);
- }
coord += ");";
return std::make_pair(coord,
@@ -1755,7 +1779,7 @@ private:
instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL;
constexpr std::array<const char*, 4> coord_container{
- {"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}};
+ {"", "int coords = (", "ivec2 coords = ivec2(", "ivec3 coords = ivec3("}};
std::string coord = coord_container[total_coord_count];
@@ -1871,8 +1895,6 @@ private:
instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
instr.fmul.tab5c68_0
.Value()); // SMO typical sends 1 here which seems to be the default
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in FMUL is not implemented");
op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
@@ -1896,20 +1918,17 @@ private:
}
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, instr.generates_cc, 0, true);
break;
}
case OpCode::Id::FADD_C:
case OpCode::Id::FADD_R:
case OpCode::Id::FADD_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in FADD is not implemented");
-
op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, instr.generates_cc, 0, true);
break;
}
case OpCode::Id::MUFU: {
@@ -1917,31 +1936,31 @@ private:
switch (instr.sub_op) {
case SubOp::Cos:
regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
case SubOp::Sin:
regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
case SubOp::Ex2:
regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
case SubOp::Lg2:
regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
case SubOp::Rcp:
regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
case SubOp::Rsq:
regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
case SubOp::Sqrt:
regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
- instr.alu.saturate_d, 0, true);
+ instr.alu.saturate_d, false, 0, true);
break;
default:
UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
@@ -1952,8 +1971,9 @@ private:
case OpCode::Id::FMNMX_C:
case OpCode::Id::FMNMX_R:
case OpCode::Id::FMNMX_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in FMNMX is not implemented");
+ UNIMPLEMENTED_IF_MSG(
+ instr.generates_cc,
+ "Condition codes generation in FMNMX is partially implemented");
op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
@@ -1964,7 +1984,7 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0,
'(' + condition + ") ? min(" + parameters + ") : max(" +
parameters + ')',
- 1, 1, false, 0, true);
+ 1, 1, false, instr.generates_cc, 0, true);
break;
}
case OpCode::Id::RRO_C:
@@ -1989,18 +2009,16 @@ private:
break;
}
case OpCode::Id::FMUL32_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
- "Condition codes generation in FMUL32 is not implemented");
-
- regs.SetRegisterToFloat(instr.gpr0, 0,
- regs.GetRegisterAsFloat(instr.gpr8) + " * " +
- GetImmediate32(instr),
- 1, 1, instr.fmul32.saturate, 0, true);
+ regs.SetRegisterToFloat(
+ instr.gpr0, 0,
+ regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1,
+ instr.fmul32.saturate, instr.op_32.generates_cc, 0, true);
break;
}
case OpCode::Id::FADD32I: {
- UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
- "Condition codes generation in FADD32I is not implemented");
+ UNIMPLEMENTED_IF_MSG(
+ instr.op_32.generates_cc,
+ "Condition codes generation in FADD32I is partially implemented");
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
std::string op_b = GetImmediate32(instr);
@@ -2021,7 +2039,8 @@ private:
op_b = "-(" + op_b + ')';
}
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false,
+ instr.op_32.generates_cc, 0, true);
break;
}
}
@@ -2035,16 +2054,14 @@ private:
switch (opcode->get().GetId()) {
case OpCode::Id::BFE_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in BFE is not implemented");
-
std::string inner_shift =
'(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
std::string outer_shift =
'(' + inner_shift + " >> " +
std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
- regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1, false,
+ instr.generates_cc);
break;
}
default: {
@@ -2055,8 +2072,6 @@ private:
break;
}
case OpCode::Type::Bfi: {
- UNIMPLEMENTED_IF(instr.generates_cc);
-
const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> {
switch (opcode->get().GetId()) {
case OpCode::Id::BFI_IMM_R:
@@ -2064,14 +2079,17 @@ private:
std::to_string(instr.alu.GetSignedImm20_20())};
default:
UNREACHABLE();
+ return {regs.GetRegisterAsInteger(instr.gpr39, 0, false),
+ std::to_string(instr.alu.GetSignedImm20_20())};
}
}();
const std::string offset = '(' + packed_shift + " & 0xff)";
const std::string bits = "((" + packed_shift + " >> 8) & 0xff)";
const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
- regs.SetRegisterToInteger(
- instr.gpr0, false, 0,
- "bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, false, 0,
+ "bitfieldInsert(" + base + ", " + insert + ", " + offset +
+ ", " + bits + ')',
+ 1, 1, false, instr.generates_cc);
break;
}
case OpCode::Type::Shift: {
@@ -2093,9 +2111,6 @@ private:
case OpCode::Id::SHR_C:
case OpCode::Id::SHR_R:
case OpCode::Id::SHR_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in SHR is not implemented");
-
if (!instr.shift.is_signed) {
// Logical shift right
op_a = "uint(" + op_a + ')';
@@ -2103,7 +2118,7 @@ private:
// Cast to int is superfluous for arithmetic shift, it's only for a logical shift
regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
- 1, 1);
+ 1, 1, false, instr.generates_cc);
break;
}
case OpCode::Id::SHL_C:
@@ -2111,7 +2126,8 @@ private:
case OpCode::Id::SHL_IMM:
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
"Condition codes generation in SHL is not implemented");
- regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1, false,
+ instr.generates_cc);
break;
default: {
UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
@@ -2125,18 +2141,17 @@ private:
switch (opcode->get().GetId()) {
case OpCode::Id::IADD32I:
- UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
- "Condition codes generation in IADD32I is not implemented");
+ UNIMPLEMENTED_IF_MSG(
+ instr.op_32.generates_cc,
+ "Condition codes generation in IADD32I is partially implemented");
if (instr.iadd32i.negate_a)
op_a = "-(" + op_a + ')';
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
- instr.iadd32i.saturate != 0);
+ instr.iadd32i.saturate, instr.op_32.generates_cc);
break;
case OpCode::Id::LOP32I: {
- UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
- "Condition codes generation in LOP32I is not implemented");
if (instr.alu.lop32i.invert_a)
op_a = "~(" + op_a + ')';
@@ -2146,7 +2161,7 @@ private:
WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
Tegra::Shader::PredicateResultMode::None,
- Tegra::Shader::Pred::UnusedIndex);
+ Tegra::Shader::Pred::UnusedIndex, instr.op_32.generates_cc);
break;
}
default: {
@@ -2175,7 +2190,7 @@ private:
case OpCode::Id::IADD_R:
case OpCode::Id::IADD_IMM: {
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in IADD is not implemented");
+ "Condition codes generation in IADD is partially implemented");
if (instr.alu_integer.negate_a)
op_a = "-(" + op_a + ')';
@@ -2184,14 +2199,15 @@ private:
op_b = "-(" + op_b + ')';
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
- instr.alu.saturate_d);
+ instr.alu.saturate_d, instr.generates_cc);
break;
}
case OpCode::Id::IADD3_C:
case OpCode::Id::IADD3_R:
case OpCode::Id::IADD3_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in IADD3 is not implemented");
+ UNIMPLEMENTED_IF_MSG(
+ instr.generates_cc,
+ "Condition codes generation in IADD3 is partially implemented");
std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
@@ -2247,14 +2263,16 @@ private:
result = '(' + op_a + " + " + op_b + " + " + op_c + ')';
}
- regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1, false,
+ instr.generates_cc);
break;
}
case OpCode::Id::ISCADD_C:
case OpCode::Id::ISCADD_R:
case OpCode::Id::ISCADD_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in ISCADD is not implemented");
+ UNIMPLEMENTED_IF_MSG(
+ instr.generates_cc,
+ "Condition codes generation in ISCADD is partially implemented");
if (instr.alu_integer.negate_a)
op_a = "-(" + op_a + ')';
@@ -2265,7 +2283,8 @@ private:
const std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
regs.SetRegisterToInteger(instr.gpr0, true, 0,
- "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
+ "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1,
+ false, instr.generates_cc);
break;
}
case OpCode::Id::POPC_C:
@@ -2289,8 +2308,6 @@ private:
case OpCode::Id::LOP_C:
case OpCode::Id::LOP_R:
case OpCode::Id::LOP_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in LOP is not implemented");
if (instr.alu.lop.invert_a)
op_a = "~(" + op_a + ')';
@@ -2299,15 +2316,13 @@ private:
op_b = "~(" + op_b + ')';
WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
- instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
+ instr.alu.lop.pred_result_mode, instr.alu.lop.pred48,
+ instr.generates_cc);
break;
}
case OpCode::Id::LOP3_C:
case OpCode::Id::LOP3_R:
case OpCode::Id::LOP3_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in LOP3 is not implemented");
-
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
std::string lut;
@@ -2317,15 +2332,16 @@ private:
lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')';
}
- WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
+ WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut, instr.generates_cc);
break;
}
case OpCode::Id::IMNMX_C:
case OpCode::Id::IMNMX_R:
case OpCode::Id::IMNMX_IMM: {
UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in IMNMX is not implemented");
+ UNIMPLEMENTED_IF_MSG(
+ instr.generates_cc,
+ "Condition codes generation in IMNMX is partially implemented");
const std::string condition =
GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
@@ -2333,7 +2349,7 @@ private:
regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
'(' + condition + ") ? min(" + parameters + ") : max(" +
parameters + ')',
- 1, 1);
+ 1, 1, false, instr.generates_cc);
break;
}
case OpCode::Id::LEA_R2:
@@ -2394,7 +2410,8 @@ private:
UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
"Unhandled LEA Predicate");
const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
- regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1, false,
+ instr.generates_cc);
break;
}
@@ -2499,7 +2516,7 @@ private:
UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
instr.ffma.tab5980_1.Value());
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in FFMA is not implemented");
+ "Condition codes generation in FFMA is partially implemented");
switch (opcode->get().GetId()) {
case OpCode::Id::FFMA_CR: {
@@ -2530,7 +2547,7 @@ private:
}
regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
- 1, 1, instr.alu.saturate_d, 0, true);
+ 1, 1, instr.alu.saturate_d, instr.generates_cc, 0, true);
break;
}
case OpCode::Type::Hfma2: {
@@ -2601,16 +2618,14 @@ private:
}
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
- 1, instr.alu.saturate_d, 0, instr.conversion.dest_size,
- instr.generates_cc.Value() != 0);
+ 1, instr.alu.saturate_d, instr.generates_cc, 0,
+ instr.conversion.dest_size);
break;
}
case OpCode::Id::I2F_R:
case OpCode::Id::I2F_C: {
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
UNIMPLEMENTED_IF(instr.conversion.selector);
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in I2F is not implemented");
std::string op_a;
if (instr.is_b_gpr) {
@@ -2633,14 +2648,12 @@ private:
op_a = "-(" + op_a + ')';
}
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, false, instr.generates_cc);
break;
}
case OpCode::Id::F2F_R: {
UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in F2F is not implemented");
std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
if (instr.conversion.abs_a) {
@@ -2672,14 +2685,13 @@ private:
break;
}
- regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
+ regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d,
+ instr.generates_cc);
break;
}
case OpCode::Id::F2I_R:
case OpCode::Id::F2I_C: {
UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in F2I is not implemented");
std::string op_a{};
if (instr.is_b_gpr) {
@@ -2722,7 +2734,8 @@ private:
}
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
- 1, false, 0, instr.conversion.dest_size);
+ 1, false, instr.generates_cc, 0,
+ instr.conversion.dest_size);
break;
}
default: {
@@ -2885,7 +2898,7 @@ private:
shader.AddLine(coord);
if (depth_compare) {
- regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
+ regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1);
} else {
shader.AddLine("vec4 texture_tmp = " + texture + ';');
std::size_t dest_elem{};
@@ -2894,7 +2907,7 @@ private:
// Skip disabled components
continue;
}
- regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
+ regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, false,
dest_elem);
++dest_elem;
}
@@ -2980,7 +2993,7 @@ private:
// Skip disabled components
continue;
}
- regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
+ regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, false,
dest_elem);
++dest_elem;
}
@@ -3229,7 +3242,7 @@ private:
}
case OpCode::Type::PredicateSetRegister: {
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in PSET is not implemented");
+ "Condition codes generation in PSET is partially implemented");
const std::string op_a =
GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
@@ -3246,10 +3259,11 @@ private:
const std::string result = '(' + predicate + ") " + combiner + " (" + second_pred + ')';
if (instr.pset.bf == 0) {
const std::string value = '(' + result + ") ? 0xFFFFFFFF : 0";
- regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, false, 0, value, 1, 1, false,
+ instr.generates_cc);
} else {
const std::string value = '(' + result + ") ? 1.0 : 0.0";
- regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1, false, instr.generates_cc);
}
break;
}
@@ -3314,6 +3328,7 @@ private:
return std::to_string(instr.r2p.immediate_mask);
default:
UNREACHABLE();
+ return std::to_string(instr.r2p.immediate_mask);
}
}();
const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
@@ -3365,14 +3380,11 @@ private:
") " + combiner + " (" + second_pred + "))";
if (instr.fset.bf) {
- regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
+ regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1, false,
+ instr.generates_cc);
} else {
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
- 1);
- }
- if (instr.generates_cc.Value() != 0) {
- regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate);
- LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete");
+ 1, false, instr.generates_cc);
}
break;
}
@@ -3459,7 +3471,7 @@ private:
UNIMPLEMENTED_IF(instr.xmad.sign_a);
UNIMPLEMENTED_IF(instr.xmad.sign_b);
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in XMAD is not implemented");
+ "Condition codes generation in XMAD is partially implemented");
std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)};
std::string op_b;
@@ -3545,7 +3557,8 @@ private:
sum = "((" + sum + " & 0xFFFF) | (" + src2 + "<< 16))";
}
- regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1, false,
+ instr.generates_cc);
break;
}
default: {
@@ -3749,8 +3762,7 @@ private:
}
regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
- instr.vmad.saturate == 1, 0, Register::Size::Word,
- instr.vmad.cc);
+ instr.vmad.saturate, instr.vmad.cc);
break;
}
case OpCode::Id::VSETP: {
@@ -3777,7 +3789,10 @@ private:
}
break;
}
- default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); }
+ default: {
+ UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName());
+ break;
+ }
}
break;
@@ -3932,4 +3947,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
return {};
}
-} // namespace OpenGL::GLShader::Decompiler \ No newline at end of file
+} // namespace OpenGL::GLShader::Decompiler
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4fd0d66c5..235732d86 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers(
// Load the framebuffer from memory, draw it to the screen, and swap buffers
LoadFBToScreenInfo(*framebuffer);
- DrawScreen();
+
+ if (renderer_settings.screenshot_requested)
+ CaptureScreenshot();
+
+ DrawScreen(render_window.GetFramebufferLayout());
+
render_window.SwapBuffers();
}
@@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
/**
* Draws the emulated screens to the emulator window.
*/
-void RendererOpenGL::DrawScreen() {
+void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
if (renderer_settings.set_background_color) {
// Update background color before drawing
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
0.0f);
}
- const auto& layout = render_window.GetFramebufferLayout();
const auto& screen = layout.screen;
glViewport(0, 0, layout.width, layout.height);
@@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() {
/// Updates the framerate
void RendererOpenGL::UpdateFramerate() {}
+void RendererOpenGL::CaptureScreenshot() {
+ // Draw the current frame to the screenshot framebuffer
+ screenshot_framebuffer.Create();
+ GLuint old_read_fb = state.draw.read_framebuffer;
+ GLuint old_draw_fb = state.draw.draw_framebuffer;
+ state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
+ state.Apply();
+
+ Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
+
+ GLuint renderbuffer;
+ glGenRenderbuffers(1, &renderbuffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
+
+ DrawScreen(layout);
+
+ glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+ renderer_settings.screenshot_bits);
+
+ screenshot_framebuffer.Release();
+ state.draw.read_framebuffer = old_read_fb;
+ state.draw.draw_framebuffer = old_draw_fb;
+ state.Apply();
+ glDeleteRenderbuffers(1, &renderbuffer);
+
+ renderer_settings.screenshot_complete_callback();
+ renderer_settings.screenshot_requested = false;
+}
+
static const char* GetSource(GLenum source) {
#define RET(s) \
case GL_DEBUG_SOURCE_##s: \
@@ -427,6 +462,7 @@ static const char* GetSource(GLenum source) {
RET(OTHER);
default:
UNREACHABLE();
+ return "Unknown source";
}
#undef RET
}
@@ -445,6 +481,7 @@ static const char* GetType(GLenum type) {
RET(MARKER);
default:
UNREACHABLE();
+ return "Unknown type";
}
#undef RET
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index c0868c0e4..b85cc262f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -16,6 +16,10 @@ namespace Core::Frontend {
class EmuWindow;
}
+namespace Layout {
+struct FramebufferLayout;
+}
+
namespace OpenGL {
/// Structure used for storing information about the textures for the Switch screen
@@ -66,10 +70,12 @@ private:
void ConfigureFramebufferTexture(TextureInfo& texture,
const Tegra::FramebufferConfig& framebuffer);
- void DrawScreen();
+ void DrawScreen(const Layout::FramebufferLayout& layout);
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
void UpdateFramerate();
+ void CaptureScreenshot();
+
// Loads framebuffer from emulated memory into the display information structure
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
// Fills active OpenGL texture with the given RGBA color.
@@ -82,6 +88,7 @@ private:
OGLVertexArray vertex_array;
OGLBuffer vertex_buffer;
OGLProgram shader;
+ OGLFramebuffer screenshot_framebuffer;
/// Display information for Switch screen
ScreenInfo screen_info;
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 9582dd2ca..a97b1562b 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -65,6 +65,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
+ return PixelFormat::S8Z24;
}
}
@@ -141,6 +142,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
+ return PixelFormat::RGBA8_SRGB;
}
}
@@ -327,6 +329,7 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
static_cast<u32>(component_type));
UNREACHABLE();
+ return PixelFormat::ABGR8U;
}
}
@@ -346,6 +349,7 @@ ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
default:
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
UNREACHABLE();
+ return ComponentType::UNorm;
}
}
@@ -393,6 +397,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
+ return ComponentType::UNorm;
}
}
@@ -403,6 +408,7 @@ PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
+ return PixelFormat::ABGR8U;
}
}
@@ -418,6 +424,7 @@ ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
+ return ComponentType::UNorm;
}
}
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index bbae9285f..5db75de22 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -226,7 +226,7 @@ u32 BytesPerPixel(TextureFormat format) {
return 8;
default:
UNIMPLEMENTED_MSG("Format not implemented");
- break;
+ return 1;
}
}
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 07e3a7d24..f7de3471b 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,6 +3,8 @@
// Refer to the license.txt file included.
#include <memory>
+#include "core/core.h"
+#include "core/settings.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h"
@@ -13,4 +15,10 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
}
+u16 GetResolutionScaleFactor(const RendererBase& renderer) {
+ return !Settings::values.resolution_factor
+ ? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
+ : Settings::values.resolution_factor;
+}
+
} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index f79f85dfe..5b373bcb1 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -22,4 +22,6 @@ class RendererBase;
*/
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
+u16 GetResolutionScaleFactor(const RendererBase& renderer);
+
} // namespace VideoCore