summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------externals/xbyak0
-rw-r--r--src/audio_core/audio_renderer.cpp12
-rw-r--r--src/audio_core/audio_renderer.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp8
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/settings.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_dma.cpp12
-rw-r--r--src/video_core/engines/shader_bytecode.h41
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp62
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp381
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h43
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp318
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h50
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_state.h8
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp20
-rw-r--r--src/video_core/textures/texture.h12
-rw-r--r--src/yuzu/CMakeLists.txt4
-rw-r--r--src/yuzu/compatibility_list.cpp18
-rw-r--r--src/yuzu/compatibility_list.h17
-rw-r--r--src/yuzu/game_list.cpp222
-rw-r--r--src/yuzu/game_list.h10
-rw-r--r--src/yuzu/game_list_p.h67
-rw-r--r--src/yuzu/game_list_worker.cpp239
-rw-r--r--src/yuzu/game_list_worker.h72
-rw-r--r--src/yuzu/main.cpp16
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
33 files changed, 1025 insertions, 646 deletions
diff --git a/externals/xbyak b/externals/xbyak
-Subproject 71b75f653f3858403eb33d48f6346eef34b837f
+Subproject 1de435ed04c8e74775804da944d176baf0ce56e
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 397b107f5..a75cd3be5 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -17,10 +17,10 @@ AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
- audio_core = std::make_unique<AudioCore::AudioOut>();
- stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
- [=]() { buffer_event->Signal(); });
- audio_core->StartStream(stream);
+ audio_out = std::make_unique<AudioCore::AudioOut>();
+ stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
+ [=]() { buffer_event->Signal(); });
+ audio_out->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
@@ -236,11 +236,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
}
}
}
- audio_core->QueueBuffer(stream, tag, std::move(buffer));
+ audio_out->QueueBuffer(stream, tag, std::move(buffer));
}
void AudioRenderer::ReleaseAndQueueBuffers() {
- const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
+ const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
for (const auto& tag : released_buffers) {
QueueMixedBuffer(tag);
}
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index eba67f28e..6d069d693 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -204,7 +204,7 @@ private:
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
- std::unique_ptr<AudioCore::AudioOut> audio_core;
+ std::unique_ptr<AudioCore::AudioOut> audio_out;
AudioCore::StreamPtr stream;
};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 0d31abe8b..a8e0c869f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <atomic>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -78,7 +77,7 @@ private:
SharedMemory mem{};
std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
- if (is_device_reload_pending.exchange(false))
+ if (Settings::values.is_device_reload_pending.exchange(false))
LoadInputDevices();
// Set up controllers as neon red+blue Joy-Con attached to console
@@ -267,7 +266,6 @@ private:
CoreTiming::EventType* pad_update_event;
// Stored input state info
- std::atomic<bool> is_device_reload_pending{true};
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
@@ -797,7 +795,9 @@ public:
}
};
-void ReloadInputDevices() {}
+void ReloadInputDevices() {
+ Settings::values.is_device_reload_pending.store(true);
+}
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<Hid>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 9d804652e..9bb7c7b26 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -74,8 +74,6 @@ using Kernel::SharedPtr;
namespace Service {
-std::unordered_map<std::string, SharedPtr<ClientPort>> g_kernel_named_ports;
-
/**
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
diff --git a/src/core/settings.h b/src/core/settings.h
index 5bf1863e6..08a16ef2c 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <atomic>
#include <string>
#include "common/common_types.h"
@@ -120,6 +121,7 @@ struct Values {
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
std::string motion_device;
std::string touch_device;
+ std::atomic_bool is_device_reload_pending{true};
// Core
bool use_cpu_jit;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index e63ad4d46..1308080b5 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -293,10 +293,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
"TIC versions other than BlockLinear or Pitch are unimplemented");
- ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) ||
- (tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap),
- "Texture types other than Texture2D are unimplemented");
-
auto r_type = tic_entry.r_type.Value();
auto g_type = tic_entry.g_type.Value();
auto b_type = tic_entry.b_type.Value();
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 6e740713f..c24d33d5c 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -41,7 +41,6 @@ void MaxwellDMA::HandleCopy() {
// TODO(Subv): Perform more research and implement all features of this engine.
ASSERT(regs.exec.enable_swizzle == 0);
- ASSERT(regs.exec.enable_2d == 1);
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
@@ -51,10 +50,19 @@ void MaxwellDMA::HandleCopy() {
ASSERT(regs.dst_params.pos_y == 0);
if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
- Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count * regs.y_count);
+ size_t copy_size = regs.x_count;
+
+ // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
+ // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count).
+ if (regs.exec.enable_2d) {
+ copy_size = copy_size * regs.y_count;
+ }
+
+ Memory::CopyBlock(dest_cpu, source_cpu, copy_size);
return;
}
+ ASSERT(regs.exec.enable_2d == 1);
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index d2388673e..9176a8dbc 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -244,6 +244,16 @@ enum class TextureType : u64 {
TextureCube = 3,
};
+enum class TextureQueryType : u64 {
+ Dimension = 1,
+ TextureType = 2,
+ SamplePosition = 5,
+ Filter = 16,
+ LevelOfDetail = 18,
+ Wrap = 20,
+ BorderColor = 22,
+};
+
enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
@@ -519,6 +529,21 @@ union Instruction {
} tex;
union {
+ BitField<22, 6, TextureQueryType> query_type;
+ BitField<31, 4, u64> component_mask;
+ } txq;
+
+ union {
+ BitField<28, 1, u64> array;
+ BitField<29, 2, TextureType> texture_type;
+ BitField<31, 4, u64> component_mask;
+
+ bool IsComponentEnabled(size_t component) const {
+ return ((1ull << component) & component_mask) != 0;
+ }
+ } tmml;
+
+ union {
BitField<28, 1, u64> array;
BitField<29, 2, TextureType> texture_type;
BitField<56, 2, u64> component;
@@ -670,11 +695,13 @@ public:
LDG, // Load from global memory
STG, // Store in global memory
TEX,
- TEXQ, // Texture Query
- TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
- TLDS, // Texture Load with scalar/non-vec4 source/destinations
- TLD4, // Texture Load 4
- TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations
+ TXQ, // Texture Query
+ TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
+ TLDS, // Texture Load with scalar/non-vec4 source/destinations
+ TLD4, // Texture Load 4
+ TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations
+ TMML_B, // Texture Mip Map Level
+ TMML, // Texture Mip Map Level
EXIT,
IPA,
FFMA_IMM, // Fused Multiply and Add
@@ -894,11 +921,13 @@ private:
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
- INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"),
+ INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"),
INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"),
INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"),
+ INST("110111110110----", Id::TMML_B, Type::Memory, "TMML_B"),
+ INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 19399fab8..e6d6917fa 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -151,11 +151,6 @@ void RasterizerOpenGL::SetupVertexArrays() {
Tegra::GPUVAddr start = vertex_array.StartAddress();
const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
- if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
- start += static_cast<Tegra::GPUVAddr>(vertex_array.stride) *
- (gpu.state.current_instance / vertex_array.divisor);
- }
-
ASSERT(end > start);
const u64 size = end - start + 1;
const GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
@@ -165,10 +160,8 @@ void RasterizerOpenGL::SetupVertexArrays() {
vertex_array.stride);
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
- // Tell OpenGL that this is an instanced vertex buffer to prevent accessing different
- // indexes on each vertex. We do the instance indexing manually by incrementing the
- // start address of the vertex buffer.
- glVertexBindingDivisor(index, 1);
+ // Enable vertex buffer instancing with the specified divisor.
+ glVertexBindingDivisor(index, vertex_array.divisor);
} else {
// Disable the vertex buffer instancing.
glVertexBindingDivisor(index, 0);
@@ -432,7 +425,8 @@ void RasterizerOpenGL::DrawArrays() {
return;
MICROPROFILE_SCOPE(OpenGL_Drawing);
- const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+ const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
+ const auto& regs = gpu.regs;
ScopeAcquireGLContext acquire_context{emu_window};
@@ -497,11 +491,26 @@ void RasterizerOpenGL::DrawArrays() {
index_buffer_offset += static_cast<GLintptr>(regs.index_array.first) *
static_cast<GLintptr>(regs.index_array.FormatSizeInBytes());
- glDrawElementsBaseVertex(primitive_mode, regs.index_array.count,
- MaxwellToGL::IndexFormat(regs.index_array.format),
- reinterpret_cast<const void*>(index_buffer_offset), base_vertex);
+ if (gpu.state.current_instance > 0) {
+ glDrawElementsInstancedBaseVertexBaseInstance(
+ primitive_mode, regs.index_array.count,
+ MaxwellToGL::IndexFormat(regs.index_array.format),
+ reinterpret_cast<const void*>(index_buffer_offset), 1, base_vertex,
+ gpu.state.current_instance);
+ } else {
+ glDrawElementsBaseVertex(primitive_mode, regs.index_array.count,
+ MaxwellToGL::IndexFormat(regs.index_array.format),
+ reinterpret_cast<const void*>(index_buffer_offset),
+ base_vertex);
+ }
} else {
- glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count);
+ if (gpu.state.current_instance > 0) {
+ glDrawArraysInstancedBaseInstance(primitive_mode, regs.vertex_buffer.first,
+ regs.vertex_buffer.count, 1,
+ gpu.state.current_instance);
+ } else {
+ glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count);
+ }
}
// Disable scissor test
@@ -518,13 +527,9 @@ void RasterizerOpenGL::DrawArrays() {
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {}
-void RasterizerOpenGL::FlushAll() {
- MICROPROFILE_SCOPE(OpenGL_CacheManagement);
-}
+void RasterizerOpenGL::FlushAll() {}
-void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
- MICROPROFILE_SCOPE(OpenGL_CacheManagement);
-}
+void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {}
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -534,7 +539,6 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
}
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
- MICROPROFILE_SCOPE(OpenGL_CacheManagement);
InvalidateRegion(addr, size);
}
@@ -582,7 +586,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create();
mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
- wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap;
+ wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap;
// default is GL_LINEAR_MIPMAP_LINEAR
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -609,8 +613,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
wrap_v = config.wrap_v;
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
}
+ if (wrap_p != config.wrap_p) {
+ wrap_p = config.wrap_p;
+ glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
+ }
- if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) {
+ if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
+ wrap_p == Tegra::Texture::WrapMode::Border) {
const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
config.border_color_b, config.border_color_a}};
if (border_color != new_border_color) {
@@ -694,14 +703,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
if (!texture.enabled) {
- state.texture_units[current_bindpoint].texture_2d = 0;
+ state.texture_units[current_bindpoint].texture = 0;
continue;
}
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
Surface surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) {
- state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle;
+ state.texture_units[current_bindpoint].texture = surface->Texture().handle;
+ state.texture_units[current_bindpoint].target = surface->Target();
state.texture_units[current_bindpoint].swizzle.r =
MaxwellToGL::SwizzleSource(texture.tic.x_source);
state.texture_units[current_bindpoint].swizzle.g =
@@ -712,7 +722,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
MaxwellToGL::SwizzleSource(texture.tic.w_source);
} else {
// Can occur when texture addr is null or its memory is unmapped/invalid
- state.texture_units[current_bindpoint].texture_2d = 0;
+ state.texture_units[current_bindpoint].texture = 0;
}
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 9c30dc0e8..c6bb1516b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -93,6 +93,7 @@ private:
Tegra::Texture::TextureFilter min_filter;
Tegra::Texture::WrapMode wrap_u;
Tegra::Texture::WrapMode wrap_v;
+ Tegra::Texture::WrapMode wrap_p;
GLvec4 border_color;
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index f6b2c5a86..fa730b9e6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -7,6 +7,7 @@
#include "common/alignment.h"
#include "common/assert.h"
+#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "core/core.h"
@@ -51,10 +52,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.type = GetFormatType(params.pixel_format);
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
+ params.depth = config.tic.Depth();
params.unaligned_height = config.tic.Height();
params.size_in_bytes = params.SizeInBytes();
- params.cache_width = Common::AlignUp(params.width, 16);
- params.cache_height = Common::AlignUp(params.height, 16);
+ params.cache_width = Common::AlignUp(params.width, 8);
+ params.cache_height = Common::AlignUp(params.height, 8);
+ params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
return params;
}
@@ -69,10 +72,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.type = GetFormatType(params.pixel_format);
params.width = config.width;
params.height = config.height;
+ params.depth = 1;
params.unaligned_height = config.height;
params.size_in_bytes = params.SizeInBytes();
- params.cache_width = Common::AlignUp(params.width, 16);
- params.cache_height = Common::AlignUp(params.height, 16);
+ params.cache_width = Common::AlignUp(params.width, 8);
+ params.cache_height = Common::AlignUp(params.height, 8);
+ params.target = SurfaceTarget::Texture2D;
return params;
}
@@ -86,13 +91,14 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.pixel_format = PixelFormatFromDepthFormat(format);
params.component_type = ComponentTypeFromDepthFormat(format);
params.type = GetFormatType(params.pixel_format);
- params.size_in_bytes = params.SizeInBytes();
params.width = zeta_width;
params.height = zeta_height;
+ params.depth = 1;
params.unaligned_height = zeta_height;
params.size_in_bytes = params.SizeInBytes();
- params.cache_width = Common::AlignUp(params.width, 16);
- params.cache_height = Common::AlignUp(params.height, 16);
+ params.cache_width = Common::AlignUp(params.width, 8);
+ params.cache_height = Common::AlignUp(params.height, 8);
+ params.target = SurfaceTarget::Texture2D;
return params;
}
@@ -166,6 +172,26 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
ComponentType::Float, false}, // Z32FS8
}};
+static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) {
+ switch (target) {
+ case SurfaceParams::SurfaceTarget::Texture1D:
+ return GL_TEXTURE_1D;
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ return GL_TEXTURE_2D;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ return GL_TEXTURE_3D;
+ case SurfaceParams::SurfaceTarget::Texture1DArray:
+ return GL_TEXTURE_1D_ARRAY;
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ return GL_TEXTURE_2D_ARRAY;
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ return GL_TEXTURE_CUBE_MAP;
+ }
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
+ UNREACHABLE();
+ return {};
+}
+
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)];
@@ -220,7 +246,8 @@ static bool IsFormatBCn(PixelFormat format) {
}
template <bool morton_to_gl, PixelFormat format>
-void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) {
+void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t gl_buffer_size,
+ VAddr addr) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
@@ -230,18 +257,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
addr, tile_size, bytes_per_pixel, stride, height, block_height);
- const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
- gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
+ const size_t size_to_copy{std::min(gl_buffer_size, data.size())};
+ memcpy(gl_buffer, data.data(), size_to_copy);
} else {
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
// check the configuration for this and perform more generic un/swizzle
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
- Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl);
+ Memory::GetPointer(addr), gl_buffer, morton_to_gl);
}
}
-static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
+static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr),
SurfaceParams::MaxPixelFormat>
morton_to_gl_fns = {
// clang-format off
@@ -298,7 +325,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
// clang-format on
};
-static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
+static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr),
SurfaceParams::MaxPixelFormat>
gl_to_morton_fns = {
// clang-format off
@@ -357,33 +384,6 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
// clang-format on
};
-// Allocate an uninitialized texture of appropriate size and format for the surface
-static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width,
- u32 height) {
- OpenGLState cur_state = OpenGLState::GetCurState();
-
- // Keep track of previous texture bindings
- GLuint old_tex = cur_state.texture_units[0].texture_2d;
- cur_state.texture_units[0].texture_2d = texture;
- cur_state.Apply();
- glActiveTexture(GL_TEXTURE0);
-
- if (!format_tuple.compressed) {
- // Only pre-create the texture for non-compressed textures.
- glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
- format_tuple.format, format_tuple.type, nullptr);
- }
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- // Restore previous texture bindings
- cur_state.texture_units[0].texture_2d = old_tex;
- cur_state.Apply();
-}
-
static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
GLuint read_fb_handle, GLuint draw_fb_handle) {
@@ -438,12 +438,56 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec
return true;
}
-CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) {
+CachedSurface::CachedSurface(const SurfaceParams& params)
+ : params(params), gl_target(SurfaceTargetToGL(params.target)) {
texture.Create();
const auto& rect{params.GetRect()};
- AllocateSurfaceTexture(texture.handle,
- GetFormatTuple(params.pixel_format, params.component_type),
- rect.GetWidth(), rect.GetHeight());
+
+ // Keep track of previous texture bindings
+ OpenGLState cur_state = OpenGLState::GetCurState();
+ const auto& old_tex = cur_state.texture_units[0];
+ SCOPE_EXIT({
+ cur_state.texture_units[0] = old_tex;
+ cur_state.Apply();
+ });
+
+ cur_state.texture_units[0].texture = texture.handle;
+ cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
+ cur_state.Apply();
+ glActiveTexture(GL_TEXTURE0);
+
+ const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
+ if (!format_tuple.compressed) {
+ // Only pre-create the texture for non-compressed textures.
+ switch (params.target) {
+ case SurfaceParams::SurfaceTarget::Texture1D:
+ glTexImage1D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
+ rect.GetWidth(), 0, format_tuple.format, format_tuple.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glTexImage2D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
+ rect.GetWidth(), rect.GetHeight(), 0, format_tuple.format,
+ format_tuple.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glTexImage3D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
+ rect.GetWidth(), rect.GetHeight(), params.depth, 0, format_tuple.format,
+ format_tuple.type, nullptr);
+ break;
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+ static_cast<u32>(params.target));
+ UNREACHABLE();
+ glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, rect.GetWidth(),
+ rect.GetHeight(), 0, format_tuple.format, format_tuple.type, nullptr);
+ }
+ }
+
+ glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL, 0);
+ glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
@@ -461,7 +505,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
S8Z24 input_pixel{};
Z24S8 output_pixel{};
- const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
+ constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const size_t offset{bpp * (y * width + x)};
@@ -474,7 +518,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
}
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
- const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)};
+ constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const size_t offset{bpp * (y * width + x)};
@@ -514,23 +558,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
}
}
-/**
- * Helper function to perform software conversion (as needed) when flushing a buffer to Switch
- * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
- * typical desktop GPUs.
- */
-static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format,
- u32 /*width*/, u32 /*height*/) {
- switch (pixel_format) {
- case PixelFormat::ASTC_2D_4X4:
- case PixelFormat::S8Z24:
- LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}",
- static_cast<u32>(pixel_format));
- UNREACHABLE();
- break;
- }
-}
-
MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
void CachedSurface::LoadGLBuffer() {
ASSERT(params.type != SurfaceType::Fill);
@@ -545,13 +572,25 @@ void CachedSurface::LoadGLBuffer() {
MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
if (params.is_tiled) {
- gl_buffer.resize(copy_size);
+ // 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) {
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ // Pass impl. to the fallback code below
+ break;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
+ static_cast<u32>(params.target));
+ UNREACHABLE();
+ }
+ gl_buffer.resize(static_cast<size_t>(params.depth) * copy_size);
morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
- params.width, params.block_height, params.height, gl_buffer, params.addr);
+ params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
+ params.addr);
} else {
- const u8* const texture_src_data_end = texture_src_data + copy_size;
-
+ const u8* const texture_src_data_end{texture_src_data +
+ (static_cast<size_t>(params.depth) * copy_size)};
gl_buffer.assign(texture_src_data, texture_src_data_end);
}
@@ -560,23 +599,7 @@ void CachedSurface::LoadGLBuffer() {
MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
void CachedSurface::FlushGLBuffer() {
- u8* const dst_buffer = Memory::GetPointer(params.addr);
-
- ASSERT(dst_buffer);
- ASSERT(gl_buffer.size() ==
- params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
-
- MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
-
- ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
- params.height);
-
- if (!params.is_tiled) {
- std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
- } else {
- gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
- params.width, params.block_height, params.height, gl_buffer, params.addr);
- }
+ ASSERT_MSG(false, "Unimplemented");
}
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -586,22 +609,29 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
MICROPROFILE_SCOPE(OpenGL_TextureUL);
- ASSERT(gl_buffer.size() ==
- params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
+ ASSERT(gl_buffer.size() == static_cast<size_t>(params.width) * params.height *
+ GetGLBytesPerPixel(params.pixel_format) * params.depth);
const auto& rect{params.GetRect()};
// Load data from memory to the surface
- GLint x0 = static_cast<GLint>(rect.left);
- GLint y0 = static_cast<GLint>(rect.bottom);
- size_t buffer_offset = (y0 * params.width + x0) * GetGLBytesPerPixel(params.pixel_format);
+ const GLint x0 = static_cast<GLint>(rect.left);
+ const GLint y0 = static_cast<GLint>(rect.bottom);
+ const size_t buffer_offset =
+ static_cast<size_t>(static_cast<size_t>(y0) * params.width + static_cast<size_t>(x0)) *
+ GetGLBytesPerPixel(params.pixel_format);
const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
- GLuint target_tex = texture.handle;
+ const GLuint target_tex = texture.handle;
OpenGLState cur_state = OpenGLState::GetCurState();
- GLuint old_tex = cur_state.texture_units[0].texture_2d;
- cur_state.texture_units[0].texture_2d = target_tex;
+ const auto& old_tex = cur_state.texture_units[0];
+ SCOPE_EXIT({
+ cur_state.texture_units[0] = old_tex;
+ cur_state.Apply();
+ });
+ cur_state.texture_units[0].texture = target_tex;
+ cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
cur_state.Apply();
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
@@ -610,74 +640,68 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
glActiveTexture(GL_TEXTURE0);
if (tuple.compressed) {
- glCompressedTexImage2D(
- GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
- static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
- &gl_buffer[buffer_offset]);
+ switch (params.target) {
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glCompressedTexImage2D(
+ SurfaceTargetToGL(params.target), 0, tuple.internal_format,
+ static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
+ static_cast<GLsizei>(params.size_in_bytes), &gl_buffer[buffer_offset]);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glCompressedTexImage3D(
+ SurfaceTargetToGL(params.target), 0, tuple.internal_format,
+ static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
+ static_cast<GLsizei>(params.depth), 0, static_cast<GLsizei>(params.size_in_bytes),
+ &gl_buffer[buffer_offset]);
+ break;
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+ static_cast<u32>(params.target));
+ UNREACHABLE();
+ glCompressedTexImage2D(
+ GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
+ static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
+ &gl_buffer[buffer_offset]);
+ }
} else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
- static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
- &gl_buffer[buffer_offset]);
- }
-
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-
- cur_state.texture_units[0].texture_2d = old_tex;
- cur_state.Apply();
-}
-
-MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64));
-void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
- if (params.type == SurfaceType::Fill)
- return;
-
- MICROPROFILE_SCOPE(OpenGL_TextureDL);
-
- gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
-
- OpenGLState state = OpenGLState::GetCurState();
- OpenGLState prev_state = state;
- SCOPE_EXIT({ prev_state.Apply(); });
- const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
-
- // Ensure no bad interactions with GL_PACK_ALIGNMENT
- ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
- glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
-
- const auto& rect{params.GetRect()};
- size_t buffer_offset =
- (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format);
-
- state.UnbindTexture(texture.handle);
- state.draw.read_framebuffer = read_fb_handle;
- state.Apply();
-
- if (params.type == SurfaceType::ColorTexture) {
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture.handle, 0);
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
- 0);
- } else if (params.type == SurfaceType::Depth) {
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
- texture.handle, 0);
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
- } else {
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
- texture.handle, 0);
+ switch (params.target) {
+ case SurfaceParams::SurfaceTarget::Texture1D:
+ glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0,
+ static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
+ &gl_buffer[buffer_offset]);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0,
+ static_cast<GLsizei>(rect.GetWidth()),
+ static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
+ &gl_buffer[buffer_offset]);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0,
+ static_cast<GLsizei>(rect.GetWidth()),
+ static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
+ tuple.type, &gl_buffer[buffer_offset]);
+ break;
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+ static_cast<u32>(params.target));
+ UNREACHABLE();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
+ static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
+ &gl_buffer[buffer_offset]);
+ }
}
- glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
- static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
- tuple.format, tuple.type, &gl_buffer[buffer_offset]);
- glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
read_framebuffer.Create();
draw_framebuffer.Create();
+ copy_pbo.Create();
}
Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
@@ -748,7 +772,6 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
}
void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
- surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
surface->FlushGLBuffer();
}
@@ -809,8 +832,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
// If format is unchanged, we can do a faster blit without reinterpreting pixel data
if (params.pixel_format == new_params.pixel_format) {
BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
- new_surface->GetSurfaceParams().GetRect(), params.type,
- read_framebuffer.handle, draw_framebuffer.handle);
+ params.GetRect(), params.type, read_framebuffer.handle,
+ draw_framebuffer.handle);
return new_surface;
}
@@ -821,12 +844,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
- // Use a Pixel Buffer Object to download the previous texture and then upload it to the new
- // one using the new format.
- OGLBuffer pbo;
- pbo.Create();
-
- glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle);
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
if (source_format.compressed) {
glGetCompressedTextureImage(surface->Texture().handle, 0,
@@ -845,8 +863,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
// of the data in this case. Games like Super Mario Odyssey seem to hit this case
// when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
// but it doesn't clear it beforehand, the texture is already full of zeros.
- LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during "
- "reinterpretation but the texture is tiled.");
+ LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
+ "reinterpretation but the texture is tiled.");
}
size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
std::vector<u8> data(remaining_size);
@@ -859,21 +877,38 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
const auto& dest_rect{new_params.GetRect()};
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle);
if (dest_format.compressed) {
- glCompressedTexSubImage2D(
- GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()),
- static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
- static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
+ LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!");
+ UNREACHABLE();
} else {
- glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
- static_cast<GLsizei>(dest_rect.GetWidth()),
- static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
- dest_format.type, nullptr);
+ switch (new_params.target) {
+ case SurfaceParams::SurfaceTarget::Texture1D:
+ glTextureSubImage1D(new_surface->Texture().handle, 0, 0,
+ static_cast<GLsizei>(dest_rect.GetWidth()), dest_format.format,
+ dest_format.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
+ static_cast<GLsizei>(dest_rect.GetWidth()),
+ static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
+ dest_format.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glTextureSubImage3D(new_surface->Texture().handle, 0, 0, 0, 0,
+ static_cast<GLsizei>(dest_rect.GetWidth()),
+ static_cast<GLsizei>(dest_rect.GetHeight()),
+ static_cast<GLsizei>(new_params.depth), dest_format.format,
+ dest_format.type, nullptr);
+ break;
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+ static_cast<u32>(params.target));
+ UNREACHABLE();
+ }
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-
- pbo.Release();
}
return new_surface;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index aad75f200..8312b2c7a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -109,6 +109,33 @@ struct SurfaceParams {
Invalid = 4,
};
+ enum class SurfaceTarget {
+ Texture1D,
+ Texture2D,
+ Texture3D,
+ Texture1DArray,
+ Texture2DArray,
+ TextureCubemap,
+ };
+
+ static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
+ switch (texture_type) {
+ case Tegra::Texture::TextureType::Texture1D:
+ return SurfaceTarget::Texture1D;
+ case Tegra::Texture::TextureType::Texture2D:
+ case Tegra::Texture::TextureType::Texture2DNoMipmap:
+ return SurfaceTarget::Texture2D;
+ case Tegra::Texture::TextureType::Texture1DArray:
+ return SurfaceTarget::Texture1DArray;
+ case Tegra::Texture::TextureType::Texture2DArray:
+ return SurfaceTarget::Texture2DArray;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
+ UNREACHABLE();
+ return SurfaceTarget::Texture2D;
+ }
+ }
+
/**
* Gets the compression factor for the specified PixelFormat. This applies to just the
* "compressed width" and "compressed height", not the overall compression factor of a
@@ -635,7 +662,7 @@ struct SurfaceParams {
ASSERT(width % compression_factor == 0);
ASSERT(height % compression_factor == 0);
return (width / compression_factor) * (height / compression_factor) *
- GetFormatBpp(pixel_format) / CHAR_BIT;
+ GetFormatBpp(pixel_format) * depth / CHAR_BIT;
}
/// Creates SurfaceParams from a texture configuration
@@ -664,8 +691,10 @@ struct SurfaceParams {
SurfaceType type;
u32 width;
u32 height;
+ u32 depth;
u32 unaligned_height;
size_t size_in_bytes;
+ SurfaceTarget target;
// Parameters used for caching only
u32 cache_width;
@@ -709,6 +738,10 @@ public:
return texture;
}
+ GLenum Target() const {
+ return gl_target;
+ }
+
static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
if (format == SurfaceParams::PixelFormat::Invalid)
return 0;
@@ -724,14 +757,14 @@ public:
void LoadGLBuffer();
void FlushGLBuffer();
- // Upload/Download data in gl_buffer in/to this surface's texture
+ // Upload data in gl_buffer to this surface's texture
void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
- void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
private:
OGLTexture texture;
std::vector<u8> gl_buffer;
SurfaceParams params;
+ GLenum gl_target;
};
class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -774,6 +807,10 @@ private:
OGLFramebuffer read_framebuffer;
OGLFramebuffer draw_framebuffer;
+
+ /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
+ /// using the new format.
+ OGLBuffer copy_pbo;
};
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7e4b85ac3..61080f5cc 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -13,8 +13,8 @@ namespace OpenGL {
/// Gets the address for the specified shader stage program
static VAddr GetShaderAddress(Maxwell::ShaderProgram program) {
- auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
- auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)];
+ const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
+ const auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)];
return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() +
shader_config.offset);
}
@@ -86,7 +86,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
}
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
- auto search{resource_cache.find(buffer.GetHash())};
+ const auto search{resource_cache.find(buffer.GetHash())};
if (search == resource_cache.end()) {
const GLuint index{
glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, buffer.GetName().c_str())};
@@ -98,7 +98,7 @@ GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& b
}
GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
- auto search{uniform_cache.find(sampler.GetHash())};
+ const auto search{uniform_cache.find(sampler.GetHash())};
if (search == uniform_cache.end()) {
const GLint index{glGetUniformLocation(program.handle, sampler.GetName().c_str())};
uniform_cache[sampler.GetHash()] = index;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 841647ebe..762e58aad 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -113,7 +113,7 @@ private:
/// Scans a range of code for labels and determines the exit method.
ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) {
- auto [iter, inserted] =
+ const auto [iter, inserted] =
exit_method_map.emplace(std::make_pair(begin, end), ExitMethod::Undetermined);
ExitMethod& exit_method = iter->second;
if (!inserted)
@@ -131,22 +131,22 @@ private:
if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
return exit_method = ExitMethod::AlwaysEnd;
} else {
- ExitMethod not_met = Scan(offset + 1, end, labels);
+ const ExitMethod not_met = Scan(offset + 1, end, labels);
return exit_method = ParallelExit(ExitMethod::AlwaysEnd, not_met);
}
}
case OpCode::Id::BRA: {
- u32 target = offset + instr.bra.GetBranchTarget();
+ const u32 target = offset + instr.bra.GetBranchTarget();
labels.insert(target);
- ExitMethod no_jmp = Scan(offset + 1, end, labels);
- ExitMethod jmp = Scan(target, end, labels);
+ const ExitMethod no_jmp = Scan(offset + 1, end, labels);
+ const ExitMethod jmp = Scan(target, end, labels);
return exit_method = ParallelExit(no_jmp, jmp);
}
case OpCode::Id::SSY: {
// The SSY instruction uses a similar encoding as the BRA instruction.
ASSERT_MSG(instr.bra.constant_buffer == 0,
"Constant buffer SSY is not supported");
- u32 target = offset + instr.bra.GetBranchTarget();
+ const u32 target = offset + instr.bra.GetBranchTarget();
labels.insert(target);
// Continue scanning for an exit method.
break;
@@ -346,8 +346,8 @@ public:
*/
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
const Tegra::Shader::IpaMode& input_mode) {
- std::string dest = GetRegisterAsFloat(reg);
- std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem);
+ const std::string dest = GetRegisterAsFloat(reg);
+ const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem);
shader.AddLine(dest + " = " + src + ';');
}
@@ -359,8 +359,8 @@ public:
* @param reg The register to use as the source value.
*/
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) {
- std::string dest = GetOutputAttribute(attribute);
- std::string src = GetRegisterAsFloat(reg);
+ const std::string dest = GetOutputAttribute(attribute);
+ const std::string src = GetRegisterAsFloat(reg);
if (!dest.empty()) {
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
@@ -393,9 +393,9 @@ public:
GLSLRegister::Type type) {
declr_const_buffers[cbuf_index].MarkAsUsedIndirect(cbuf_index, stage);
- std::string final_offset = fmt::format("({} + {})", index_str, offset / 4);
- std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" +
- final_offset + " % 4]";
+ const std::string final_offset = fmt::format("({} + {})", index_str, offset / 4);
+ const std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" +
+ final_offset + " % 4]";
if (type == GLSLRegister::Type::Float) {
return value;
@@ -443,13 +443,12 @@ public:
}
declarations.AddNewLine();
- // Append the sampler2D array for the used textures.
- const size_t num_samplers = used_samplers.size();
- if (num_samplers > 0) {
- declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' +
- std::to_string(num_samplers) + "];");
- declarations.AddNewLine();
+ const auto& samplers = GetSamplers();
+ for (const auto& sampler : samplers) {
+ declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
+ ';');
}
+ declarations.AddNewLine();
}
/// Returns a list of constant buffer declarations
@@ -461,27 +460,29 @@ public:
}
/// Returns a list of samplers used in the shader
- std::vector<SamplerEntry> GetSamplers() const {
+ 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) {
- size_t offset = static_cast<size_t>(sampler.index.Value());
+ std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
+ bool is_array) {
+ const size_t offset = static_cast<size_t>(sampler.index.Value());
// If this sampler has already been used, return the existing mapping.
- auto itr =
+ 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);
return itr->GetName();
}
// Otherwise create a new mapping for this sampler
- size_t next_index = used_samplers.size();
- SamplerEntry entry{stage, offset, next_index};
+ const size_t next_index = used_samplers.size();
+ const SamplerEntry entry{stage, offset, next_index, type, is_array};
used_samplers.emplace_back(entry);
return entry.GetName();
}
@@ -698,7 +699,7 @@ private:
};
bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
- u32 bit = render_target * 4 + component;
+ const u32 bit = render_target * 4 + component;
return enabled_color_outputs & (1 << bit);
}
};
@@ -706,7 +707,7 @@ private:
/// Gets the Subroutine object corresponding to the specified address.
const Subroutine& GetSubroutine(u32 begin, u32 end) const {
- auto iter = subroutines.find(Subroutine{begin, end, suffix});
+ const auto iter = subroutines.find(Subroutine{begin, end, suffix});
ASSERT(iter != subroutines.end());
return *iter;
}
@@ -722,8 +723,8 @@ private:
}
/// Generates code representing a texture sampler.
- std::string GetSampler(const Sampler& sampler) {
- return regs.AccessSampler(sampler);
+ std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
+ return regs.AccessSampler(sampler, type, is_array);
}
/**
@@ -751,7 +752,7 @@ private:
// Can't assign to the constant predicate.
ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
- std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
+ const std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
shader.AddLine(variable + " = " + value + ';');
declr_predicates.insert(std::move(variable));
}
@@ -1032,7 +1033,11 @@ private:
if (header.writes_depth) {
// The depth output is always 2 registers after the last color output, and current_reg
// already contains one past the last color register.
- shader.AddLine("gl_FragDepth = " + regs.GetRegisterAsFloat(current_reg + 1) + ';');
+
+ shader.AddLine(
+ "gl_FragDepth = " +
+ regs.GetRegisterAsFloat(static_cast<Tegra::Shader::Register>(current_reg) + 1) +
+ ';');
}
}
@@ -1434,7 +1439,7 @@ private:
if (instr.alu_integer.negate_b)
op_b = "-(" + op_b + ')';
- std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
+ 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);
@@ -1452,7 +1457,7 @@ private:
case OpCode::Id::SEL_C:
case OpCode::Id::SEL_R:
case OpCode::Id::SEL_IMM: {
- std::string condition =
+ const std::string condition =
GetPredicateCondition(instr.sel.pred, instr.sel.neg_pred != 0);
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + condition + ") ? " + op_a + " : " + op_b, 1, 1);
@@ -1474,8 +1479,9 @@ private:
case OpCode::Id::LOP3_C:
case OpCode::Id::LOP3_R:
case OpCode::Id::LOP3_IMM: {
- std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
+ const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
std::string lut;
+
if (opcode->GetId() == OpCode::Id::LOP3_R) {
lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')';
} else {
@@ -1490,9 +1496,9 @@ private:
case OpCode::Id::IMNMX_IMM: {
ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None,
"Unimplemented");
- std::string condition =
+ const std::string condition =
GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
- std::string parameters = op_a + ',' + op_b;
+ const std::string parameters = op_a + ',' + op_b;
regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0,
'(' + condition + ") ? min(" + parameters + ") : max(" +
parameters + ')',
@@ -1509,7 +1515,7 @@ private:
break;
}
case OpCode::Type::Ffma: {
- std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
std::string op_b = instr.ffma.negate_b ? "-" : "";
std::string op_c = instr.ffma.negate_c ? "-" : "";
@@ -1719,7 +1725,7 @@ private:
shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
" / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);");
- std::string op_a =
+ const std::string op_a =
regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, "index",
GLSLRegister::Type::Float);
@@ -1729,7 +1735,7 @@ private:
break;
case Tegra::Shader::UniformType::Double: {
- std::string op_b =
+ const std::string op_b =
regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4,
"index", GLSLRegister::Type::Float);
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
@@ -1753,10 +1759,35 @@ private:
break;
}
case OpCode::Id::TEX: {
- const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
- const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
- const std::string sampler = GetSampler(instr.sampler);
- const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
+ ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
+ Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
+ std::string coord;
+
+ switch (texture_type) {
+ case Tegra::Shader::TextureType::Texture1D: {
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ coord = "float coords = " + x + ';';
+ break;
+ }
+ case Tegra::Shader::TextureType::Texture2D: {
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+ break;
+ }
+ default:
+ LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+ static_cast<u32>(texture_type));
+ UNREACHABLE();
+
+ // Fallback to interpreting as a 2D texture for now
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+ texture_type = Tegra::Shader::TextureType::Texture2D;
+ }
+
+ const std::string sampler = GetSampler(instr.sampler, texture_type, false);
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{");
@@ -1778,20 +1809,65 @@ private:
break;
}
case OpCode::Id::TEXS: {
- const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
- const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
- const std::string sampler = GetSampler(instr.sampler);
- const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
+ std::string coord;
+ Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
+ bool is_array{instr.texs.IsArrayTexture()};
+
+ switch (texture_type) {
+ case Tegra::Shader::TextureType::Texture2D: {
+ if (is_array) {
+ const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
+ coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
+ } else {
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
+ coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+ }
+ break;
+ }
+ default:
+ LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+ static_cast<u32>(texture_type));
+ UNREACHABLE();
+ // Fallback to interpreting as a 2D texture for now
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
+ coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+ texture_type = Tegra::Shader::TextureType::Texture2D;
+ is_array = false;
+ }
+ const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
const std::string texture = "texture(" + sampler + ", coords)";
WriteTexsInstruction(instr, coord, texture);
break;
}
case OpCode::Id::TLDS: {
- const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
- const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20);
- const std::string sampler = GetSampler(instr.sampler);
- const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
+ ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D);
+ ASSERT(instr.tlds.IsArrayTexture() == false);
+ std::string coord;
+
+ switch (instr.tlds.GetTextureType()) {
+ case Tegra::Shader::TextureType::Texture2D: {
+ if (instr.tlds.IsArrayTexture()) {
+ LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture");
+ UNREACHABLE();
+ } else {
+ const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
+ const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
+ coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
+ }
+ break;
+ }
+ default:
+ LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+ static_cast<u32>(instr.tlds.GetTextureType()));
+ UNREACHABLE();
+ }
+ const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(),
+ instr.tlds.IsArrayTexture());
const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
WriteTexsInstruction(instr, coord, texture);
break;
@@ -1799,12 +1875,12 @@ private:
case OpCode::Id::TLD4: {
ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
ASSERT(instr.tld4.array == 0);
- std::string coord{};
+ std::string coord;
switch (instr.tld4.texture_type) {
case Tegra::Shader::TextureType::Texture2D: {
- std::string x = regs.GetRegisterAsFloat(instr.gpr8);
- std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
break;
}
@@ -1814,7 +1890,8 @@ private:
UNREACHABLE();
}
- const std::string sampler = GetSampler(instr.sampler);
+ const std::string sampler =
+ GetSampler(instr.sampler, instr.tld4.texture_type, false);
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{");
@@ -1840,13 +1917,82 @@ private:
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
- const std::string sampler = GetSampler(instr.sampler);
+ const std::string sampler =
+ GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
const std::string texture = "textureGather(" + sampler + ", coords, " +
std::to_string(instr.tld4s.component) + ')';
WriteTexsInstruction(instr, coord, texture);
break;
}
+ case OpCode::Id::TXQ: {
+ // TODO: the new commits on the texture refactor, change the way samplers work.
+ // Sadly, not all texture instructions specify the type of texture their sampler
+ // uses. This must be fixed at a later instance.
+ const std::string sampler =
+ GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
+ switch (instr.txq.query_type) {
+ case Tegra::Shader::TextureQueryType::Dimension: {
+ const std::string texture = "textureQueryLevels(" + sampler + ')';
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1);
+ break;
+ }
+ default: {
+ LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}",
+ static_cast<u32>(instr.txq.query_type.Value()));
+ UNREACHABLE();
+ }
+ }
+ break;
+ }
+ case OpCode::Id::TMML: {
+ const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
+ const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ const bool is_array = instr.tmml.array != 0;
+ auto texture_type = instr.tmml.texture_type.Value();
+ const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
+
+ // TODO: add coordinates for different samplers once other texture types are
+ // implemented.
+ 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);
+ coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+ break;
+ }
+ default:
+ LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+ static_cast<u32>(texture_type));
+ 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);
+ coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+ texture_type = Tegra::Shader::TextureType::Texture2D;
+ }
+ // Add an extra scope and declare the texture coords inside to prevent
+ // overwriting them in case they are used as outputs of the texs instruction.
+ shader.AddLine('{');
+ ++shader.scope;
+ shader.AddLine(coord);
+ const std::string texture = "textureQueryLod(" + sampler + ", coords)";
+ const std::string tmp = "vec2 tmp = " + texture + "*vec2(256.0, 256.0);";
+ shader.AddLine(tmp);
+
+ regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(tmp.y)", 1, 1);
+ regs.SetRegisterToInteger(instr.gpr0.Value() + 1, false, 0, "uint(tmp.x)", 1, 1);
+ --shader.scope;
+ shader.AddLine('}');
+ break;
+ }
default: {
LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName());
UNREACHABLE();
@@ -1886,12 +2032,12 @@ private:
// We can't use the constant predicate as destination.
ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
- std::string second_pred =
+ const std::string second_pred =
GetPredicateCondition(instr.fsetp.pred39, instr.fsetp.neg_pred != 0);
- std::string combiner = GetPredicateCombiner(instr.fsetp.op);
+ const std::string combiner = GetPredicateCombiner(instr.fsetp.op);
- std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b);
+ const std::string predicate = GetPredicateComparison(instr.fsetp.cond, op_a, op_b);
// Set the primary predicate to the result of Predicate OP SecondPredicate
SetPredicate(instr.fsetp.pred3,
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
@@ -1905,7 +2051,8 @@ private:
break;
}
case OpCode::Type::IntegerSetPredicate: {
- std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed);
+ const std::string op_a =
+ regs.GetRegisterAsInteger(instr.gpr8, 0, instr.isetp.is_signed);
std::string op_b;
if (instr.is_b_imm) {
@@ -1922,12 +2069,12 @@ private:
// We can't use the constant predicate as destination.
ASSERT(instr.isetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
- std::string second_pred =
+ const std::string second_pred =
GetPredicateCondition(instr.isetp.pred39, instr.isetp.neg_pred != 0);
- std::string combiner = GetPredicateCombiner(instr.isetp.op);
+ const std::string combiner = GetPredicateCombiner(instr.isetp.op);
- std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b);
+ const std::string predicate = GetPredicateComparison(instr.isetp.cond, op_a, op_b);
// Set the primary predicate to the result of Predicate OP SecondPredicate
SetPredicate(instr.isetp.pred3,
'(' + predicate + ") " + combiner + " (" + second_pred + ')');
@@ -1941,20 +2088,20 @@ private:
break;
}
case OpCode::Type::PredicateSetPredicate: {
- std::string op_a =
+ const std::string op_a =
GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
- std::string op_b =
+ const std::string op_b =
GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0);
// We can't use the constant predicate as destination.
ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
- std::string second_pred =
+ const std::string second_pred =
GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0);
- std::string combiner = GetPredicateCombiner(instr.psetp.op);
+ const std::string combiner = GetPredicateCombiner(instr.psetp.op);
- std::string predicate =
+ const std::string predicate =
'(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')';
// Set the primary predicate to the result of Predicate OP SecondPredicate
@@ -1980,7 +2127,7 @@ private:
std::string op_b = instr.fset.neg_b ? "-" : "";
if (instr.is_b_imm) {
- std::string imm = GetImmediate19(instr);
+ const std::string imm = GetImmediate19(instr);
if (instr.fset.neg_imm)
op_b += "(-" + imm + ')';
else
@@ -2000,13 +2147,14 @@ private:
// The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
// condition is true, and to 0 otherwise.
- std::string second_pred =
+ const std::string second_pred =
GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0);
- std::string combiner = GetPredicateCombiner(instr.fset.op);
+ const std::string combiner = GetPredicateCombiner(instr.fset.op);
- std::string predicate = "((" + GetPredicateComparison(instr.fset.cond, op_a, op_b) +
- ") " + combiner + " (" + second_pred + "))";
+ const std::string predicate = "((" +
+ GetPredicateComparison(instr.fset.cond, op_a, op_b) +
+ ") " + combiner + " (" + second_pred + "))";
if (instr.fset.bf) {
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
@@ -2017,7 +2165,7 @@ private:
break;
}
case OpCode::Type::IntegerSet: {
- std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed);
+ const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed);
std::string op_b;
@@ -2034,13 +2182,14 @@ private:
// The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
// condition is true, and to 0 otherwise.
- std::string second_pred =
+ const std::string second_pred =
GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0);
- std::string combiner = GetPredicateCombiner(instr.iset.op);
+ const std::string combiner = GetPredicateCombiner(instr.iset.op);
- std::string predicate = "((" + GetPredicateComparison(instr.iset.cond, op_a, op_b) +
- ") " + combiner + " (" + second_pred + "))";
+ const std::string predicate = "((" +
+ GetPredicateComparison(instr.iset.cond, op_a, op_b) +
+ ") " + combiner + " (" + second_pred + "))";
if (instr.iset.bf) {
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
@@ -2190,7 +2339,7 @@ private:
case OpCode::Id::BRA: {
ASSERT_MSG(instr.bra.constant_buffer == 0,
"BRA with constant buffers are not implemented");
- u32 target = offset + instr.bra.GetBranchTarget();
+ const u32 target = offset + instr.bra.GetBranchTarget();
shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
break;
}
@@ -2214,7 +2363,7 @@ private:
// has a similar structure to the BRA opcode.
ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
- u32 target = offset + instr.bra.GetBranchTarget();
+ const u32 target = offset + instr.bra.GetBranchTarget();
EmitPushToSSYStack(target);
break;
}
@@ -2308,10 +2457,10 @@ private:
shader.AddLine("case " + std::to_string(label) + "u: {");
++shader.scope;
- auto next_it = labels.lower_bound(label + 1);
- u32 next_label = next_it == labels.end() ? subroutine.end : *next_it;
+ const auto next_it = labels.lower_bound(label + 1);
+ const u32 next_label = next_it == labels.end() ? subroutine.end : *next_it;
- u32 compile_end = CompileRange(label, next_label);
+ const u32 compile_end = CompileRange(label, next_label);
if (compile_end > next_label && compile_end != PROGRAM_END) {
// This happens only when there is a label inside a IF/LOOP block
shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }");
@@ -2374,7 +2523,8 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix) {
try {
- auto subroutines = ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
+ const auto subroutines =
+ ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix);
return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
} catch (const DecompileFail& exception) {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index cbb2090ea..a43e2997b 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -9,6 +9,7 @@
#include <vector>
#include "common/common_types.h"
+#include "video_core/engines/shader_bytecode.h"
namespace OpenGL::GLShader {
@@ -73,8 +74,9 @@ class SamplerEntry {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
public:
- SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index)
- : offset(offset), stage(stage), sampler_index(index) {}
+ SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index,
+ Tegra::Shader::TextureType type, bool is_array)
+ : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
size_t GetOffset() const {
return offset;
@@ -89,8 +91,41 @@ public:
}
std::string GetName() const {
- return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' +
- std::to_string(sampler_index) + ']';
+ return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' +
+ std::to_string(sampler_index);
+ }
+
+ std::string GetTypeString() const {
+ using Tegra::Shader::TextureType;
+ std::string glsl_type;
+
+ switch (type) {
+ case TextureType::Texture1D:
+ glsl_type = "sampler1D";
+ break;
+ case TextureType::Texture2D:
+ glsl_type = "sampler2D";
+ break;
+ case TextureType::Texture3D:
+ glsl_type = "sampler3D";
+ break;
+ case TextureType::TextureCube:
+ glsl_type = "samplerCube";
+ break;
+ default:
+ UNIMPLEMENTED();
+ }
+ if (is_array)
+ glsl_type += "Array";
+ return glsl_type;
+ }
+
+ Tegra::Shader::TextureType GetType() const {
+ return type;
+ }
+
+ bool IsArray() const {
+ return is_array;
}
u32 GetHash() const {
@@ -105,11 +140,14 @@ private:
static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
"tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
};
+
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
/// instruction.
size_t offset;
- Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
- size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
+ Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
+ size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
+ Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
+ bool is_array; ///< Whether the texture is being sampled as an array texture or not.
};
struct ShaderEntries {
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 5781d9d16..5f3fe067e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -25,7 +25,7 @@ GLuint LoadShader(const char* source, GLenum type) {
default:
UNREACHABLE();
}
- GLuint shader_id = glCreateShader(type);
+ const GLuint shader_id = glCreateShader(type);
glShaderSource(shader_id, 1, &source, nullptr);
LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
glCompileShader(shader_id);
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 60a4defd1..6f70deb96 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -200,9 +200,9 @@ void OpenGLState::Apply() const {
const auto& texture_unit = texture_units[i];
const auto& cur_state_texture_unit = cur_state.texture_units[i];
- if (texture_unit.texture_2d != cur_state_texture_unit.texture_2d) {
+ if (texture_unit.texture != cur_state_texture_unit.texture) {
glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum());
- glBindTexture(GL_TEXTURE_2D, texture_unit.texture_2d);
+ glBindTexture(texture_unit.target, texture_unit.texture);
}
if (texture_unit.sampler != cur_state_texture_unit.sampler) {
glBindSampler(static_cast<GLuint>(i), texture_unit.sampler);
@@ -214,7 +214,7 @@ void OpenGLState::Apply() const {
texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) {
std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g,
texture_unit.swizzle.b, texture_unit.swizzle.a};
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
+ glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
}
}
@@ -287,7 +287,7 @@ void OpenGLState::Apply() const {
OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
for (auto& unit : texture_units) {
- if (unit.texture_2d == handle) {
+ if (unit.texture == handle) {
unit.Unbind();
}
}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 46e96a97d..e3e24b9e7 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -94,8 +94,9 @@ public:
// 3 texture units - one for each that is used in PICA fragment shader emulation
struct TextureUnit {
- GLuint texture_2d; // GL_TEXTURE_BINDING_2D
- GLuint sampler; // GL_SAMPLER_BINDING
+ GLuint texture; // GL_TEXTURE_BINDING_2D
+ GLuint sampler; // GL_SAMPLER_BINDING
+ GLenum target;
struct {
GLint r; // GL_TEXTURE_SWIZZLE_R
GLint g; // GL_TEXTURE_SWIZZLE_G
@@ -104,7 +105,7 @@ public:
} swizzle;
void Unbind() {
- texture_2d = 0;
+ texture = 0;
swizzle.r = GL_RED;
swizzle.g = GL_GREEN;
swizzle.b = GL_BLUE;
@@ -114,6 +115,7 @@ public:
void Reset() {
Unbind();
sampler = 0;
+ target = GL_TEXTURE_2D;
}
};
std::array<TextureUnit, 32> texture_units;
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index e565afcee..aadf68f16 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -29,7 +29,7 @@ OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coh
if (GLAD_GL_ARB_buffer_storage) {
persistent = true;
coherent = prefer_coherent;
- GLbitfield flags =
+ const GLbitfield flags =
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
glBufferStorage(gl_target, allocate_size, nullptr, flags);
mapped_ptr = static_cast<u8*>(glMapBufferRange(
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 411a73d50..ccff3e342 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -177,7 +177,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
Memory::GetPointer(framebuffer_addr),
gl_framebuffer_data.data(), true);
- state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
+ state.texture_units[0].texture = screen_info.texture.resource.handle;
state.Apply();
glActiveTexture(GL_TEXTURE0);
@@ -194,7 +194,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- state.texture_units[0].texture_2d = 0;
+ state.texture_units[0].texture = 0;
state.Apply();
}
}
@@ -205,7 +205,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
*/
void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
const TextureInfo& texture) {
- state.texture_units[0].texture_2d = texture.resource.handle;
+ state.texture_units[0].texture = texture.resource.handle;
state.Apply();
glActiveTexture(GL_TEXTURE0);
@@ -214,7 +214,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
// Update existing texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
- state.texture_units[0].texture_2d = 0;
+ state.texture_units[0].texture = 0;
state.Apply();
}
@@ -260,7 +260,7 @@ void RendererOpenGL::InitOpenGLObjects() {
// Allocation of storage is deferred until the first frame, when we
// know the framebuffer size.
- state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
+ state.texture_units[0].texture = screen_info.texture.resource.handle;
state.Apply();
glActiveTexture(GL_TEXTURE0);
@@ -272,7 +272,7 @@ void RendererOpenGL::InitOpenGLObjects() {
screen_info.display_texture = screen_info.texture.resource.handle;
- state.texture_units[0].texture_2d = 0;
+ state.texture_units[0].texture = 0;
state.Apply();
// Clear screen to black
@@ -305,14 +305,14 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
UNREACHABLE();
}
- state.texture_units[0].texture_2d = texture.resource.handle;
+ state.texture_units[0].texture = texture.resource.handle;
state.Apply();
glActiveTexture(GL_TEXTURE0);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
texture.gl_format, texture.gl_type, nullptr);
- state.texture_units[0].texture_2d = 0;
+ state.texture_units[0].texture = 0;
state.Apply();
}
@@ -354,14 +354,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
}};
- state.texture_units[0].texture_2d = screen_info.display_texture;
+ state.texture_units[0].texture = screen_info.display_texture;
state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- state.texture_units[0].texture_2d = 0;
+ state.texture_units[0].texture = 0;
state.Apply();
}
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index c6bd2f4b9..c2fb824b2 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -170,8 +170,12 @@ struct TICEntry {
BitField<0, 16, u32> width_minus_1;
BitField<23, 4, TextureType> texture_type;
};
- u16 height_minus_1;
- INSERT_PADDING_BYTES(10);
+ union {
+ BitField<0, 16, u32> height_minus_1;
+ BitField<16, 15, u32> depth_minus_1;
+ };
+
+ INSERT_PADDING_BYTES(8);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
@@ -192,6 +196,10 @@ struct TICEntry {
return height_minus_1 + 1;
}
+ u32 Depth() const {
+ return depth_minus_1 + 1;
+ }
+
u32 BlockHeight() const {
ASSERT(header_version == TICHeaderVersion::BlockLinear ||
header_version == TICHeaderVersion::BlockLinearColorKey);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index ea9ea69e4..f48b69809 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -9,6 +9,8 @@ add_executable(yuzu
about_dialog.h
bootmanager.cpp
bootmanager.h
+ compatibility_list.cpp
+ compatibility_list.h
configuration/config.cpp
configuration/config.h
configuration/configure_audio.cpp
@@ -43,6 +45,8 @@ add_executable(yuzu
game_list.cpp
game_list.h
game_list_p.h
+ game_list_worker.cpp
+ game_list_worker.h
hotkeys.cpp
hotkeys.h
main.cpp
diff --git a/src/yuzu/compatibility_list.cpp b/src/yuzu/compatibility_list.cpp
new file mode 100644
index 000000000..2d2cfd03c
--- /dev/null
+++ b/src/yuzu/compatibility_list.cpp
@@ -0,0 +1,18 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include <fmt/format.h>
+
+#include "yuzu/compatibility_list.h"
+
+CompatibilityList::const_iterator FindMatchingCompatibilityEntry(
+ const CompatibilityList& compatibility_list, u64 program_id) {
+ return std::find_if(compatibility_list.begin(), compatibility_list.end(),
+ [program_id](const auto& element) {
+ std::string pid = fmt::format("{:016X}", program_id);
+ return element.first == pid;
+ });
+}
diff --git a/src/yuzu/compatibility_list.h b/src/yuzu/compatibility_list.h
new file mode 100644
index 000000000..bc0175bd3
--- /dev/null
+++ b/src/yuzu/compatibility_list.h
@@ -0,0 +1,17 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+
+#include <QString>
+
+#include "common/common_types.h"
+
+using CompatibilityList = std::unordered_map<std::string, std::pair<QString, QString>>;
+
+CompatibilityList::const_iterator FindMatchingCompatibilityEntry(
+ const CompatibilityList& compatibility_list, u64 program_id);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index a3b841684..8c6e16d47 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -18,17 +18,11 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
-#include "core/file_sys/content_archive.h"
-#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
-#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/romfs.h"
-#include "core/file_sys/vfs_real.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/loader/loader.h"
+#include "yuzu/compatibility_list.h"
#include "yuzu/game_list.h"
#include "yuzu/game_list_p.h"
+#include "yuzu/game_list_worker.h"
#include "yuzu/main.h"
#include "yuzu/ui_settings.h"
@@ -436,45 +430,6 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
-static bool HasSupportedFileExtension(const std::string& file_name) {
- const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
- return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
-}
-
-static bool IsExtractedNCAMain(const std::string& file_name) {
- return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
-}
-
-static QString FormatGameName(const std::string& physical_name) {
- const QString physical_name_as_qstring = QString::fromStdString(physical_name);
- const QFileInfo file_info(physical_name_as_qstring);
-
- if (IsExtractedNCAMain(physical_name)) {
- return file_info.dir().path();
- }
-
- return physical_name_as_qstring;
-}
-
-static QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
- bool updatable = true) {
- QString out;
- for (const auto& kv : patch_manager.GetPatchVersionNames()) {
- if (!updatable && kv.first == FileSys::PatchType::Update)
- continue;
-
- if (kv.second.empty()) {
- out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str());
- } else {
- out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second)
- .c_str());
- }
- }
-
- out.chop(1);
- return out;
-}
-
void GameList::RefreshGameDirectory() {
if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
@@ -482,176 +437,3 @@ void GameList::RefreshGameDirectory() {
PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
}
}
-
-static void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager,
- const std::shared_ptr<FileSys::NCA>& nca,
- std::vector<u8>& icon, std::string& name) {
- auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
- if (icon_file != nullptr)
- icon = icon_file->ReadAllBytes();
- if (nacp != nullptr)
- name = nacp->GetApplicationName();
-}
-
-GameListWorker::GameListWorker(
- FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
- const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
- : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan),
- compatibility_list(compatibility_list) {}
-
-GameListWorker::~GameListWorker() = default;
-
-void GameListWorker::AddInstalledTitlesToGameList() {
- const auto cache = Service::FileSystem::GetUnionContents();
- const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application,
- FileSys::ContentRecordType::Program);
-
- for (const auto& game : installed_games) {
- const auto& file = cache->GetEntryUnparsed(game);
- std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
- if (!loader)
- continue;
-
- std::vector<u8> icon;
- std::string name;
- u64 program_id = 0;
- loader->ReadProgramId(program_id);
-
- const FileSys::PatchManager patch{program_id};
- const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
- if (control != nullptr)
- GetMetadataFromControlNCA(patch, control, icon, name);
-
- auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
-
- // The game list uses this as compatibility number for untested games
- QString compatibility("99");
- if (it != compatibility_list.end())
- compatibility = it->second.first;
-
- emit EntryReady({
- new GameListItemPath(
- FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
- program_id),
- new GameListItemCompat(compatibility),
- new GameListItem(FormatPatchNameVersions(patch)),
- new GameListItem(
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
- new GameListItemSize(file->GetSize()),
- });
- }
-
- const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
- FileSys::ContentRecordType::Control);
-
- for (const auto& entry : control_data) {
- const auto nca = cache->GetEntry(entry);
- if (nca != nullptr)
- nca_control_map.insert_or_assign(entry.title_id, nca);
- }
-}
-
-void GameListWorker::FillControlMap(const std::string& dir_path) {
- const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory,
- const std::string& virtual_name) -> bool {
- std::string physical_name = directory + DIR_SEP + virtual_name;
-
- if (stop_processing)
- return false; // Breaks the callback loop.
-
- bool is_dir = FileUtil::IsDirectory(physical_name);
- QFileInfo file_info(physical_name.c_str());
- if (!is_dir && file_info.suffix().toStdString() == "nca") {
- auto nca =
- std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
- if (nca->GetType() == FileSys::NCAContentType::Control)
- nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
- }
- return true;
- };
-
- FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
-}
-
-void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
- const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
- const std::string& virtual_name) -> bool {
- std::string physical_name = directory + DIR_SEP + virtual_name;
-
- if (stop_processing)
- return false; // Breaks the callback loop.
-
- bool is_dir = FileUtil::IsDirectory(physical_name);
- if (!is_dir &&
- (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
- std::unique_ptr<Loader::AppLoader> loader =
- Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
- if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
- loader->GetFileType() == Loader::FileType::Error) &&
- !UISettings::values.show_unknown))
- return true;
-
- std::vector<u8> icon;
- const auto res1 = loader->ReadIcon(icon);
-
- u64 program_id = 0;
- const auto res2 = loader->ReadProgramId(program_id);
-
- std::string name = " ";
- const auto res3 = loader->ReadTitle(name);
-
- const FileSys::PatchManager patch{program_id};
-
- if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success &&
- res2 == Loader::ResultStatus::Success) {
- // Use from metadata pool.
- if (nca_control_map.find(program_id) != nca_control_map.end()) {
- const auto nca = nca_control_map[program_id];
- GetMetadataFromControlNCA(patch, nca, icon, name);
- }
- }
-
- auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
-
- // The game list uses this as compatibility number for untested games
- QString compatibility("99");
- if (it != compatibility_list.end())
- compatibility = it->second.first;
-
- emit EntryReady({
- new GameListItemPath(
- FormatGameName(physical_name), icon, QString::fromStdString(name),
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
- program_id),
- new GameListItemCompat(compatibility),
- new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())),
- new GameListItem(
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
- new GameListItemSize(FileUtil::GetSize(physical_name)),
- });
- } else if (is_dir && recursion > 0) {
- watch_list.append(QString::fromStdString(physical_name));
- AddFstEntriesToGameList(physical_name, recursion - 1);
- }
-
- return true;
- };
-
- FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
-}
-
-void GameListWorker::run() {
- stop_processing = false;
- watch_list.append(dir_path);
- FillControlMap(dir_path.toStdString());
- AddInstalledTitlesToGameList();
- AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
- nca_control_map.clear();
- emit Finished(watch_list);
-}
-
-void GameListWorker::Cancel() {
- this->disconnect();
- stop_processing = true;
-}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 3fcb298ed..2713e7b54 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -4,8 +4,6 @@
#pragma once
-#include <unordered_map>
-
#include <QFileSystemWatcher>
#include <QHBoxLayout>
#include <QLabel>
@@ -21,6 +19,7 @@
#include <QWidget>
#include "common/common_types.h"
+#include "yuzu/compatibility_list.h"
class GameListWorker;
class GMainWindow;
@@ -90,9 +89,8 @@ signals:
void GameChosen(QString game_path);
void ShouldCancelWorker();
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
- void NavigateToGamedbEntryRequested(
- u64 program_id,
- std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
+ void NavigateToGamedbEntryRequested(u64 program_id,
+ const CompatibilityList& compatibility_list);
private slots:
void onTextChanged(const QString& newText);
@@ -114,7 +112,7 @@ private:
QStandardItemModel* item_model = nullptr;
GameListWorker* current_worker = nullptr;
QFileSystemWatcher* watcher = nullptr;
- std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list;
+ CompatibilityList compatibility_list;
};
Q_DECLARE_METATYPE(GameListOpenTarget);
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index a70a151c5..f22e422e5 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -6,9 +6,7 @@
#include <algorithm>
#include <array>
-#include <atomic>
#include <map>
-#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
@@ -16,7 +14,6 @@
#include <QCoreApplication>
#include <QImage>
#include <QObject>
-#include <QRunnable>
#include <QStandardItem>
#include <QString>
@@ -26,12 +23,6 @@
#include "yuzu/ui_settings.h"
#include "yuzu/util/util.h"
-namespace FileSys {
-class NCA;
-class RegisteredCache;
-class VfsFilesystem;
-} // namespace FileSys
-
/**
* Gets the default icon (for games without valid SMDH)
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
@@ -43,17 +34,6 @@ static QPixmap GetDefaultIcon(u32 size) {
return icon;
}
-static auto FindMatchingCompatibilityEntry(
- const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list,
- u64 program_id) {
- return std::find_if(
- compatibility_list.begin(), compatibility_list.end(),
- [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
- std::string pid = fmt::format("{:016X}", program_id);
- return element.first == pid;
- });
-}
-
class GameListItem : public QStandardItem {
public:
@@ -196,50 +176,3 @@ public:
return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong();
}
};
-
-/**
- * Asynchronous worker object for populating the game list.
- * Communicates with other threads through Qt's signal/slot system.
- */
-class GameListWorker : public QObject, public QRunnable {
- Q_OBJECT
-
-public:
- GameListWorker(
- std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan,
- const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
- ~GameListWorker() override;
-
-public slots:
- /// Starts the processing of directory tree information.
- void run() override;
- /// Tells the worker that it should no longer continue processing. Thread-safe.
- void Cancel();
-
-signals:
- /**
- * The `EntryReady` signal is emitted once an entry has been prepared and is ready
- * to be added to the game list.
- * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry.
- */
- void EntryReady(QList<QStandardItem*> entry_items);
-
- /**
- * After the worker has traversed the game directory looking for entries, this signal is emmited
- * with a list of folders that should be watched for changes as well.
- */
- void Finished(QStringList watch_list);
-
-private:
- std::shared_ptr<FileSys::VfsFilesystem> vfs;
- std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
- QStringList watch_list;
- QString dir_path;
- bool deep_scan;
- const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list;
- std::atomic_bool stop_processing;
-
- void AddInstalledTitlesToGameList();
- void FillControlMap(const std::string& dir_path);
- void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
-};
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
new file mode 100644
index 000000000..e228d61bd
--- /dev/null
+++ b/src/yuzu/game_list_worker.cpp
@@ -0,0 +1,239 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <QDir>
+#include <QFileInfo>
+
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/mode.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/loader/loader.h"
+#include "yuzu/compatibility_list.h"
+#include "yuzu/game_list.h"
+#include "yuzu/game_list_p.h"
+#include "yuzu/game_list_worker.h"
+#include "yuzu/ui_settings.h"
+
+namespace {
+void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager,
+ const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon,
+ std::string& name) {
+ auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
+ if (icon_file != nullptr)
+ icon = icon_file->ReadAllBytes();
+ if (nacp != nullptr)
+ name = nacp->GetApplicationName();
+}
+
+bool HasSupportedFileExtension(const std::string& file_name) {
+ const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
+ return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
+}
+
+bool IsExtractedNCAMain(const std::string& file_name) {
+ return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
+}
+
+QString FormatGameName(const std::string& physical_name) {
+ const QString physical_name_as_qstring = QString::fromStdString(physical_name);
+ const QFileInfo file_info(physical_name_as_qstring);
+
+ if (IsExtractedNCAMain(physical_name)) {
+ return file_info.dir().path();
+ }
+
+ return physical_name_as_qstring;
+}
+
+QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) {
+ QString out;
+ for (const auto& kv : patch_manager.GetPatchVersionNames()) {
+ if (!updatable && kv.first == FileSys::PatchType::Update)
+ continue;
+
+ if (kv.second.empty()) {
+ out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str());
+ } else {
+ out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second)
+ .c_str());
+ }
+ }
+
+ out.chop(1);
+ return out;
+}
+} // Anonymous namespace
+
+GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
+ const CompatibilityList& compatibility_list)
+ : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan),
+ compatibility_list(compatibility_list) {}
+
+GameListWorker::~GameListWorker() = default;
+
+void GameListWorker::AddInstalledTitlesToGameList() {
+ const auto cache = Service::FileSystem::GetUnionContents();
+ const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application,
+ FileSys::ContentRecordType::Program);
+
+ for (const auto& game : installed_games) {
+ const auto& file = cache->GetEntryUnparsed(game);
+ std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
+ if (!loader)
+ continue;
+
+ std::vector<u8> icon;
+ std::string name;
+ u64 program_id = 0;
+ loader->ReadProgramId(program_id);
+
+ const FileSys::PatchManager patch{program_id};
+ const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
+ if (control != nullptr)
+ GetMetadataFromControlNCA(patch, control, icon, name);
+
+ auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
+
+ // The game list uses this as compatibility number for untested games
+ QString compatibility("99");
+ if (it != compatibility_list.end())
+ compatibility = it->second.first;
+
+ emit EntryReady({
+ new GameListItemPath(
+ FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
+ program_id),
+ new GameListItemCompat(compatibility),
+ new GameListItem(FormatPatchNameVersions(patch)),
+ new GameListItem(
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
+ new GameListItemSize(file->GetSize()),
+ });
+ }
+
+ const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
+ FileSys::ContentRecordType::Control);
+
+ for (const auto& entry : control_data) {
+ const auto nca = cache->GetEntry(entry);
+ if (nca != nullptr)
+ nca_control_map.insert_or_assign(entry.title_id, nca);
+ }
+}
+
+void GameListWorker::FillControlMap(const std::string& dir_path) {
+ const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory,
+ const std::string& virtual_name) -> bool {
+ std::string physical_name = directory + DIR_SEP + virtual_name;
+
+ if (stop_processing)
+ return false; // Breaks the callback loop.
+
+ bool is_dir = FileUtil::IsDirectory(physical_name);
+ QFileInfo file_info(physical_name.c_str());
+ if (!is_dir && file_info.suffix().toStdString() == "nca") {
+ auto nca =
+ std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
+ if (nca->GetType() == FileSys::NCAContentType::Control)
+ nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
+ }
+ return true;
+ };
+
+ FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
+}
+
+void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
+ const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
+ const std::string& virtual_name) -> bool {
+ std::string physical_name = directory + DIR_SEP + virtual_name;
+
+ if (stop_processing)
+ return false; // Breaks the callback loop.
+
+ bool is_dir = FileUtil::IsDirectory(physical_name);
+ if (!is_dir &&
+ (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
+ std::unique_ptr<Loader::AppLoader> loader =
+ Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
+ if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
+ loader->GetFileType() == Loader::FileType::Error) &&
+ !UISettings::values.show_unknown))
+ return true;
+
+ std::vector<u8> icon;
+ const auto res1 = loader->ReadIcon(icon);
+
+ u64 program_id = 0;
+ const auto res2 = loader->ReadProgramId(program_id);
+
+ std::string name = " ";
+ const auto res3 = loader->ReadTitle(name);
+
+ const FileSys::PatchManager patch{program_id};
+
+ if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success &&
+ res2 == Loader::ResultStatus::Success) {
+ // Use from metadata pool.
+ if (nca_control_map.find(program_id) != nca_control_map.end()) {
+ const auto nca = nca_control_map[program_id];
+ GetMetadataFromControlNCA(patch, nca, icon, name);
+ }
+ }
+
+ auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
+
+ // The game list uses this as compatibility number for untested games
+ QString compatibility("99");
+ if (it != compatibility_list.end())
+ compatibility = it->second.first;
+
+ emit EntryReady({
+ new GameListItemPath(
+ FormatGameName(physical_name), icon, QString::fromStdString(name),
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
+ program_id),
+ new GameListItemCompat(compatibility),
+ new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())),
+ new GameListItem(
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
+ new GameListItemSize(FileUtil::GetSize(physical_name)),
+ });
+ } else if (is_dir && recursion > 0) {
+ watch_list.append(QString::fromStdString(physical_name));
+ AddFstEntriesToGameList(physical_name, recursion - 1);
+ }
+
+ return true;
+ };
+
+ FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
+}
+
+void GameListWorker::run() {
+ stop_processing = false;
+ watch_list.append(dir_path);
+ FillControlMap(dir_path.toStdString());
+ AddInstalledTitlesToGameList();
+ AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
+ nca_control_map.clear();
+ emit Finished(watch_list);
+}
+
+void GameListWorker::Cancel() {
+ this->disconnect();
+ stop_processing = true;
+}
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
new file mode 100644
index 000000000..09d20c42f
--- /dev/null
+++ b/src/yuzu/game_list_worker.h
@@ -0,0 +1,72 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <QList>
+#include <QObject>
+#include <QRunnable>
+#include <QString>
+
+#include "common/common_types.h"
+#include "yuzu/compatibility_list.h"
+
+class QStandardItem;
+
+namespace FileSys {
+class NCA;
+class VfsFilesystem;
+} // namespace FileSys
+
+/**
+ * Asynchronous worker object for populating the game list.
+ * Communicates with other threads through Qt's signal/slot system.
+ */
+class GameListWorker : public QObject, public QRunnable {
+ Q_OBJECT
+
+public:
+ GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan,
+ const CompatibilityList& compatibility_list);
+ ~GameListWorker() override;
+
+ /// Starts the processing of directory tree information.
+ void run() override;
+
+ /// Tells the worker that it should no longer continue processing. Thread-safe.
+ void Cancel();
+
+signals:
+ /**
+ * The `EntryReady` signal is emitted once an entry has been prepared and is ready
+ * to be added to the game list.
+ * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry.
+ */
+ void EntryReady(QList<QStandardItem*> entry_items);
+
+ /**
+ * After the worker has traversed the game directory looking for entries, this signal is emitted
+ * with a list of folders that should be watched for changes as well.
+ */
+ void Finished(QStringList watch_list);
+
+private:
+ void AddInstalledTitlesToGameList();
+ void FillControlMap(const std::string& dir_path);
+ void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
+
+ std::shared_ptr<FileSys::VfsFilesystem> vfs;
+ std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
+ QStringList watch_list;
+ QString dir_path;
+ bool deep_scan;
+ const CompatibilityList& compatibility_list;
+ std::atomic_bool stop_processing;
+};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index cb8135c42..e36914f14 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -47,6 +47,7 @@
#include "video_core/debug_utils/debug_utils.h"
#include "yuzu/about_dialog.h"
#include "yuzu/bootmanager.h"
+#include "yuzu/compatibility_list.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h"
#include "yuzu/debugger/console.h"
@@ -444,6 +445,8 @@ QStringList GMainWindow::GetUnsupportedGLExtensions() {
unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
unsupported_ext.append("ARB_texture_mirror_clamp_to_edge");
+ if (!GLAD_GL_ARB_base_instance)
+ unsupported_ext.append("ARB_base_instance");
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
@@ -606,7 +609,7 @@ void GMainWindow::BootGame(const QString& filename) {
}
setWindowTitle(QString("yuzu %1| %4 | %2-%3")
- .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc,
+ .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc,
QString::fromStdString(title_name)));
render_window->show();
@@ -641,7 +644,7 @@ void GMainWindow::ShutdownGame() {
game_list->show();
game_list->setFilterFocus();
setWindowTitle(QString("yuzu %1| %2-%3")
- .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
+ .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
// Disable status bar updates
status_bar_update_timer.stop();
@@ -723,14 +726,11 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
}
-void GMainWindow::OnGameListNavigateToGamedbEntry(
- u64 program_id,
- std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) {
-
- auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
+void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
+ const CompatibilityList& compatibility_list) {
+ const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
QString directory;
-
if (it != compatibility_list.end())
directory = it->second.second;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 089ea2445..552e3e61c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -13,6 +13,7 @@
#include "common/common_types.h"
#include "core/core.h"
#include "ui_main.h"
+#include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h"
class Config;
@@ -137,9 +138,8 @@ private slots:
/// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path);
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
- void OnGameListNavigateToGamedbEntry(
- u64 program_id,
- std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
+ void OnGameListNavigateToGamedbEntry(u64 program_id,
+ const CompatibilityList& compatibility_list);
void OnMenuLoadFile();
void OnMenuLoadFolder();
void OnMenuInstallToNAND();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index c87e96b2d..2f7916256 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -91,6 +91,8 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
+ if (!GLAD_GL_ARB_base_instance)
+ unsupported_ext.push_back("ARB_base_instance");
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)