diff options
-rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/common/bit_set.h | 244 | ||||
-rw-r--r-- | src/core/hle/kernel/handle_table.cpp | 11 | ||||
-rw-r--r-- | src/core/hle/kernel/handle_table.h | 15 | ||||
-rw-r--r-- | src/core/hle/service/audio/audout_u.cpp | 28 | ||||
-rw-r--r-- | src/core/hle/service/audio/audout_u.h | 3 | ||||
-rw-r--r-- | src/core/hle/service/hid/controllers/debug_pad.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 13 | ||||
-rw-r--r-- | src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | 6 | ||||
-rw-r--r-- | src/video_core/engines/maxwell_3d.h | 7 | ||||
-rw-r--r-- | src/video_core/engines/shader_bytecode.h | 1 | ||||
-rw-r--r-- | src/video_core/macro_interpreter.cpp | 29 | ||||
-rw-r--r-- | src/video_core/macro_interpreter.h | 4 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 31 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 1 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 169 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 5 |
17 files changed, 228 insertions, 345 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 895ee53f1..a5e71d879 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -44,7 +44,6 @@ add_library(common STATIC detached_tasks.cpp detached_tasks.h bit_field.h - bit_set.h cityhash.cpp cityhash.h color.h diff --git a/src/common/bit_set.h b/src/common/bit_set.h deleted file mode 100644 index 5cd1352b2..000000000 --- a/src/common/bit_set.h +++ /dev/null @@ -1,244 +0,0 @@ -// This file is under the public domain. - -#pragma once - -#include <cstddef> -#ifdef _WIN32 -#include <intrin.h> -#endif -#include <initializer_list> -#include <new> -#include <type_traits> -#include "common/common_types.h" - -// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly -namespace Common { - -// Helper functions: - -#ifdef _MSC_VER -template <typename T> -static inline int CountSetBits(T v) { - // from https://graphics.stanford.edu/~seander/bithacks.html - // GCC has this built in, but MSVC's intrinsic will only emit the actual - // POPCNT instruction, which we're not depending on - v = v - ((v >> 1) & (T) ~(T)0 / 3); - v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3); - v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15; - return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; -} -static inline int LeastSignificantSetBit(u8 val) { - unsigned long index; - _BitScanForward(&index, val); - return (int)index; -} -static inline int LeastSignificantSetBit(u16 val) { - unsigned long index; - _BitScanForward(&index, val); - return (int)index; -} -static inline int LeastSignificantSetBit(u32 val) { - unsigned long index; - _BitScanForward(&index, val); - return (int)index; -} -static inline int LeastSignificantSetBit(u64 val) { - unsigned long index; - _BitScanForward64(&index, val); - return (int)index; -} -#else -static inline int CountSetBits(u8 val) { - return __builtin_popcount(val); -} -static inline int CountSetBits(u16 val) { - return __builtin_popcount(val); -} -static inline int CountSetBits(u32 val) { - return __builtin_popcount(val); -} -static inline int CountSetBits(u64 val) { - return __builtin_popcountll(val); -} -static inline int LeastSignificantSetBit(u8 val) { - return __builtin_ctz(val); -} -static inline int LeastSignificantSetBit(u16 val) { - return __builtin_ctz(val); -} -static inline int LeastSignificantSetBit(u32 val) { - return __builtin_ctz(val); -} -static inline int LeastSignificantSetBit(u64 val) { - return __builtin_ctzll(val); -} -#endif - -// Similar to std::bitset, this is a class which encapsulates a bitset, i.e. -// using the set bits of an integer to represent a set of integers. Like that -// class, it acts like an array of bools: -// BitSet32 bs; -// bs[1] = true; -// but also like the underlying integer ([0] = least significant bit): -// BitSet32 bs2 = ...; -// bs = (bs ^ bs2) & BitSet32(0xffff); -// The following additional functionality is provided: -// - Construction using an initializer list. -// BitSet bs { 1, 2, 4, 8 }; -// - Efficiently iterating through the set bits: -// for (int i : bs) -// [i is the *index* of a set bit] -// (This uses the appropriate CPU instruction to find the next set bit in one -// operation.) -// - Counting set bits using .Count() - see comment on that method. - -// TODO: use constexpr when MSVC gets out of the Dark Ages - -template <typename IntTy> -class BitSet { - static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types"); - -public: - // A reference to a particular bit, returned from operator[]. - class Ref { - public: - Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} - Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} - operator bool() const { - return (m_bs->m_val & m_mask) != 0; - } - bool operator=(bool set) { - m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); - return set; - } - - private: - BitSet* m_bs; - IntTy m_mask; - }; - - // A STL-like iterator is required to be able to use range-based for loops. - class Iterator { - public: - Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {} - Iterator(IntTy val) : m_val(val), m_bit(0) {} - Iterator& operator=(Iterator other) { - new (this) Iterator(other); - return *this; - } - int operator*() { - return m_bit + ComputeLsb(); - } - Iterator& operator++() { - int lsb = ComputeLsb(); - m_val >>= lsb + 1; - m_bit += lsb + 1; - m_has_lsb = false; - return *this; - } - Iterator operator++(int _) { - Iterator other(*this); - ++*this; - return other; - } - bool operator==(Iterator other) const { - return m_val == other.m_val; - } - bool operator!=(Iterator other) const { - return m_val != other.m_val; - } - - private: - int ComputeLsb() { - if (!m_has_lsb) { - m_lsb = LeastSignificantSetBit(m_val); - m_has_lsb = true; - } - return m_lsb; - } - IntTy m_val; - int m_bit; - int m_lsb = -1; - bool m_has_lsb = false; - }; - - BitSet() : m_val(0) {} - explicit BitSet(IntTy val) : m_val(val) {} - BitSet(std::initializer_list<int> init) { - m_val = 0; - for (int bit : init) - m_val |= (IntTy)1 << bit; - } - - static BitSet AllTrue(std::size_t count) { - return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); - } - - Ref operator[](std::size_t bit) { - return Ref(this, (IntTy)1 << bit); - } - const Ref operator[](std::size_t bit) const { - return (*const_cast<BitSet*>(this))[bit]; - } - bool operator==(BitSet other) const { - return m_val == other.m_val; - } - bool operator!=(BitSet other) const { - return m_val != other.m_val; - } - bool operator<(BitSet other) const { - return m_val < other.m_val; - } - bool operator>(BitSet other) const { - return m_val > other.m_val; - } - BitSet operator|(BitSet other) const { - return BitSet(m_val | other.m_val); - } - BitSet operator&(BitSet other) const { - return BitSet(m_val & other.m_val); - } - BitSet operator^(BitSet other) const { - return BitSet(m_val ^ other.m_val); - } - BitSet operator~() const { - return BitSet(~m_val); - } - BitSet& operator|=(BitSet other) { - return *this = *this | other; - } - BitSet& operator&=(BitSet other) { - return *this = *this & other; - } - BitSet& operator^=(BitSet other) { - return *this = *this ^ other; - } - operator u32() = delete; - operator bool() { - return m_val != 0; - } - - // Warning: Even though on modern CPUs this is a single fast instruction, - // Dolphin's official builds do not currently assume POPCNT support on x86, - // so slower explicit bit twiddling is generated. Still should generally - // be faster than a loop. - unsigned int Count() const { - return CountSetBits(m_val); - } - - Iterator begin() const { - return Iterator(m_val); - } - Iterator end() const { - return Iterator(0); - } - - IntTy m_val; -}; - -} // namespace Common - -typedef Common::BitSet<u8> BitSet8; -typedef Common::BitSet<u16> BitSet16; -typedef Common::BitSet<u32> BitSet32; -typedef Common::BitSet<u64> BitSet64; diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 5ee5c05e3..1bf79b692 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -12,12 +12,23 @@ #include "core/hle/kernel/thread.h" namespace Kernel { +namespace { +constexpr u16 GetSlot(Handle handle) { + return handle >> 15; +} + +constexpr u16 GetGeneration(Handle handle) { + return handle & 0x7FFF; +} +} // Anonymous namespace HandleTable::HandleTable() { next_generation = 1; Clear(); } +HandleTable::~HandleTable() = default; + ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { DEBUG_ASSERT(obj != nullptr); diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index 9e2f33e8a..e3f3e3fb8 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -43,6 +43,7 @@ enum KernelHandle : Handle { class HandleTable final : NonCopyable { public: HandleTable(); + ~HandleTable(); /** * Allocates a handle for the given object. @@ -89,18 +90,8 @@ public: void Clear(); private: - /** - * This is the maximum limit of handles allowed per process in CTR-OS. It can be further - * reduced by ExHeader values, but this is not emulated here. - */ - static const std::size_t MAX_COUNT = 4096; - - static u16 GetSlot(Handle handle) { - return handle >> 15; - } - static u16 GetGeneration(Handle handle) { - return handle & 0x7FFF; - } + /// This is the maximum limit of handles allowed per process in Horizon + static constexpr std::size_t MAX_COUNT = 1024; /// Stores the Object referenced by the handle or null if the slot is empty. std::array<SharedPtr<Object>, MAX_COUNT> objects; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index ff1edefbb..23e1f1165 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -44,8 +44,10 @@ enum class AudioState : u32 { class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) - : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) { + IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, + std::string&& unique_name) + : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params), + device_name(std::move(device_name)) { static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, @@ -69,7 +71,7 @@ public: Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, - "IAudioOut", [=]() { buffer_event->Signal(); }); + std::move(unique_name), [=]() { buffer_event->Signal(); }); } private: @@ -177,6 +179,7 @@ private: AudioCore::AudioOut& audio_core; AudioCore::StreamPtr stream; + std::string device_name; AudoutParams audio_params{}; @@ -199,7 +202,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - ctx.WriteBuffer(DefaultDevice); + const auto device_name_data{ctx.ReadBuffer()}; + std::string device_name; + if (device_name_data[0] != '\0') { + device_name.assign(device_name_data.begin(), device_name_data.end()); + } else { + device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); + } + ctx.WriteBuffer(device_name); + IPC::RequestParser rp{ctx}; auto params{rp.PopRaw<AudoutParams>()}; if (params.channel_count <= 2) { @@ -212,10 +223,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { params.sample_rate = DefaultSampleRate; } - // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl - // will likely need to be updated as well. - ASSERT_MSG(!audio_out_interface, "Unimplemented"); - audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core); + std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; + auto audio_out_interface = std::make_shared<IAudioOut>( + params, *audio_core, std::move(device_name), std::move(unique_name)); IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -224,6 +234,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); + + audio_out_interfaces.push_back(std::move(audio_out_interface)); } AudOutU::AudOutU() : ServiceFramework("audout:u") { diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index dcaf64708..aed4c43b2 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -4,6 +4,7 @@ #pragma once +#include <vector> #include "core/hle/service/service.h" namespace AudioCore { @@ -24,7 +25,7 @@ public: ~AudOutU() override; private: - std::shared_ptr<IAudioOut> audio_out_interface; + std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; std::unique_ptr<AudioCore::AudioOut> audio_core; void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index e76c83aee..c22357d8c 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -71,8 +71,9 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { void Controller_DebugPad::OnLoadInputDevices() { std::transform(Settings::values.debug_pad_buttons.begin(), - Settings::values.debug_pad_buttons.end(), buttons.begin(), - Input::CreateDevice<Input::ButtonDevice>); + Settings::values.debug_pad_buttons.begin() + + Settings::NativeButton::NUM_BUTTONS_HID, + buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); std::transform(Settings::values.debug_pad_analogs.begin(), Settings::values.debug_pad_analogs.end(), analogs.begin(), Input::CreateDevice<Input::AnalogDevice>); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 7a88ae029..792d26e52 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -5,6 +5,8 @@ #include <cstring> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" namespace Service::Nvidia::Devices { @@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec return ZBCQueryTable(input, output); case IoctlCommand::IocFlushL2: return FlushL2(input, output); + case IoctlCommand::IocGetGpuTime: + return GetGpuTime(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -169,4 +173,13 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp return 0; } +u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlGetGpuTime params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks()); + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 3bbf028ad..240435eea 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -156,6 +156,11 @@ private: }; static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); + struct IoctlGetGpuTime { + u64_le gpu_time; + }; + static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); + u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); @@ -164,6 +169,7 @@ private: u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 9e480dc39..eff6abd55 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -389,6 +389,13 @@ public: ReverseSubtract = 3, Min = 4, Max = 5, + + // These values are used by Nouveau and some games. + AddGL = 0x8006, + SubtractGL = 0x8007, + ReverseSubtractGL = 0x8008, + MinGL = 0x800a, + MaxGL = 0x800b }; enum class Factor : u32 { diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 83a6fd875..c5f502ce1 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -153,6 +153,7 @@ enum class PredCondition : u64 { NotEqual = 5, GreaterEqual = 6, LessThanWithNan = 9, + LessEqualWithNan = 11, GreaterThanWithNan = 12, NotEqualWithNan = 13, GreaterEqualWithNan = 14, diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 335a8d407..2b0dea5cd 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -35,6 +35,7 @@ void MacroInterpreter::Reset() { // The next parameter index starts at 1, because $r1 already has the value of the first // parameter. next_parameter_index = 1; + carry_flag = false; } bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { @@ -135,14 +136,28 @@ MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const { return {macro_memory[offset + pc / sizeof(u32)]}; } -u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { +u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) { switch (operation) { - case ALUOperation::Add: - return src_a + src_b; - // TODO(Subv): Implement AddWithCarry - case ALUOperation::Subtract: - return src_a - src_b; - // TODO(Subv): Implement SubtractWithBorrow + case ALUOperation::Add: { + const u64 result{static_cast<u64>(src_a) + src_b}; + carry_flag = result > 0xffffffff; + return static_cast<u32>(result); + } + case ALUOperation::AddWithCarry: { + const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; + carry_flag = result > 0xffffffff; + return static_cast<u32>(result); + } + case ALUOperation::Subtract: { + const u64 result{static_cast<u64>(src_a) - src_b}; + carry_flag = result < 0x100000000; + return static_cast<u32>(result); + } + case ALUOperation::SubtractWithBorrow: { + const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; + carry_flag = result < 0x100000000; + return static_cast<u32>(result); + } case ALUOperation::Xor: return src_a ^ src_b; case ALUOperation::Or: diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index 62d1ce289..cde360288 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h @@ -117,7 +117,7 @@ private: bool Step(u32 offset, bool is_delay_slot); /// Calculates the result of an ALU operation. src_a OP src_b; - u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; + u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b); /// Performs the result operation on the input result and stores it in the specified register /// (if necessary). @@ -165,5 +165,7 @@ private: std::vector<u32> parameters; /// Index of the next parameter that will be fetched by the 'parm' instruction. u32 next_parameter_index = 0; + + bool carry_flag{}; }; } // namespace Tegra diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9ca82c06c..b994e89dd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1275,6 +1275,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) { return surface; } +void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface, + const Surface& dst_surface) { + const auto& init_params{src_surface->GetSurfaceParams()}; + const auto& dst_params{dst_surface->GetSurfaceParams()}; + VAddr address = init_params.addr; + const std::size_t layer_size = dst_params.LayerMemorySize(); + for (u32 layer = 0; layer < dst_params.depth; layer++) { + for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { + const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); + const Surface& copy = TryGet(sub_address); + if (!copy) + continue; + const auto& src_params{copy->GetSurfaceParams()}; + const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))}; + const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))}; + + glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, + 0, 0, dst_surface->Texture().handle, + SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width, + height, 1); + } + address += layer_size; + } +} + void RasterizerCacheOpenGL::FermiCopySurface( const Tegra::Engines::Fermi2D::Regs::Surface& src_config, const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { @@ -1340,11 +1365,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, CopySurface(old_surface, new_surface, copy_pbo.handle); } break; - case SurfaceTarget::TextureCubemap: case SurfaceTarget::Texture3D: + AccurateCopySurface(old_surface, new_surface); + break; + case SurfaceTarget::TextureCubemap: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: - AccurateCopySurface(old_surface, new_surface); + FastLayeredCopySurface(old_surface, new_surface); break; default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 494f6b903..9ac79c5a4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -350,6 +350,7 @@ private: /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); + void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface); /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have /// previously been used. This is to prevent surfaces from being constantly created and diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 90a88b91a..ba80e5832 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1059,11 +1059,17 @@ private: const std::string& op_a, const std::string& op_b) const { using Tegra::Shader::PredCondition; static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { - {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, - {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, - {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, - {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, - {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; + {PredCondition::LessThan, "<"}, + {PredCondition::Equal, "=="}, + {PredCondition::LessEqual, "<="}, + {PredCondition::GreaterThan, ">"}, + {PredCondition::NotEqual, "!="}, + {PredCondition::GreaterEqual, ">="}, + {PredCondition::LessThanWithNan, "<"}, + {PredCondition::NotEqualWithNan, "!="}, + {PredCondition::LessEqualWithNan, "<="}, + {PredCondition::GreaterThanWithNan, ">"}, + {PredCondition::GreaterEqualWithNan, ">="}}; const auto& comparison{PredicateComparisonStrings.find(condition)}; UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(), @@ -1072,6 +1078,7 @@ private: std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; if (condition == PredCondition::LessThanWithNan || condition == PredCondition::NotEqualWithNan || + condition == PredCondition::LessEqualWithNan || condition == PredCondition::GreaterThanWithNan || condition == PredCondition::GreaterEqualWithNan) { predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; @@ -1243,14 +1250,7 @@ private: regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); } - void WriteTexsInstruction(const Instruction& instr, const std::string& coord, - const std::string& texture) { - // 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); - + void WriteTexsInstruction(const Instruction& instr, const std::string& texture) { // TEXS has two destination registers and a swizzle. The first two elements in the swizzle // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 @@ -1273,9 +1273,6 @@ private: ++written_components; } - - --shader.scope; - shader.AddLine('}'); } static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { @@ -2563,7 +2560,6 @@ private: } // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias // or lod. - std::string op_c; const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); @@ -2586,27 +2582,35 @@ private: } case Tegra::Shader::TextureProcessMode::LB: case Tegra::Shader::TextureProcessMode::LBA: { - if (depth_compare) { - if (is_array) - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); - else - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - } else { - op_c = regs.GetRegisterAsFloat(instr.gpr20); - } + const std::string bias = [&]() { + if (depth_compare) { + if (is_array) + return regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); + else + return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + } else { + return regs.GetRegisterAsFloat(instr.gpr20); + } + }(); + shader.AddLine("float bias = " + bias + ';'); + // TODO: Figure if A suffix changes the equation at all. - texture = "texture(" + sampler + ", coords, " + op_c + ')'; + texture = "texture(" + sampler + ", coords, bias)"; break; } case Tegra::Shader::TextureProcessMode::LL: case Tegra::Shader::TextureProcessMode::LLA: { - if (num_coordinates <= 2) { - op_c = regs.GetRegisterAsFloat(instr.gpr20); - } else { - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - } + const std::string lod = [&]() { + if (num_coordinates <= 2) { + return regs.GetRegisterAsFloat(instr.gpr20); + } else { + return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + } + }(); + shader.AddLine("float lod = " + lod + ';'); + // TODO: Figure if A suffix changes the equation at all. - texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; + texture = "textureLod(" + sampler + ", coords, lod)"; break; } default: { @@ -2633,7 +2637,6 @@ private: break; } case OpCode::Id::TEXS: { - std::string coord; Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; bool is_array{instr.texs.IsArrayTexture()}; @@ -2646,17 +2649,21 @@ private: if (depth_compare) num_coordinates += 1; + // Scope to avoid variable name overlaps. + shader.AddLine('{'); + ++shader.scope; + switch (num_coordinates) { case 2: { 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 + ");"; + shader.AddLine("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 + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); } break; } @@ -2666,13 +2673,13 @@ private: const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = - "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");"; + shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + + index + ");"); } else { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); } break; } @@ -2683,7 +2690,7 @@ private: // 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 + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); texture_type = Tegra::Shader::TextureType::Texture2D; is_array = false; } @@ -2715,14 +2722,16 @@ private: } } if (!depth_compare) { - WriteTexsInstruction(instr, coord, texture); + WriteTexsInstruction(instr, texture); } else { - WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + WriteTexsInstruction(instr, "vec4(" + texture + ')'); } + + shader.AddLine('}'); + --shader.scope; break; } case OpCode::Id::TLDS: { - std::string coord; const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; const bool is_array{instr.tlds.IsArrayTexture()}; @@ -2736,12 +2745,16 @@ private: UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), "MZ is not implemented"); - u32 op_c_offset = 0; + u32 extra_op_offset = 0; + + // Scope to avoid variable name overlaps. + shader.AddLine('{'); + ++shader.scope; switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { const std::string x = regs.GetRegisterAsInteger(instr.gpr8); - coord = "int coords = " + x + ';'; + shader.AddLine("int coords = " + x + ';'); break; } case Tegra::Shader::TextureType::Texture2D: { @@ -2749,8 +2762,8 @@ private: const std::string x = regs.GetRegisterAsInteger(instr.gpr8); const std::string y = regs.GetRegisterAsInteger(instr.gpr20); - coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; - op_c_offset = 1; + shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); + extra_op_offset = 1; break; } default: @@ -2765,9 +2778,10 @@ private: break; } case Tegra::Shader::TextureProcessMode::LL: { - const std::string op_c = - regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); - texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; + shader.AddLine( + "float lod = " + + regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';'); + texture = "texelFetch(" + sampler + ", coords, lod)"; break; } default: { @@ -2776,7 +2790,10 @@ private: static_cast<u32>(instr.tlds.GetTextureProcessMode())); } } - WriteTexsInstruction(instr, coord, texture); + WriteTexsInstruction(instr, texture); + + --shader.scope; + shader.AddLine('}'); break; } case OpCode::Id::TLD4: { @@ -2799,18 +2816,23 @@ private: if (depth_compare) num_coordinates += 1; + // 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; + switch (num_coordinates) { case 2: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); break; } case 3: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); break; } default: @@ -2818,17 +2840,13 @@ private: static_cast<u32>(num_coordinates)); const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); texture_type = Tegra::Shader::TextureType::Texture2D; } const std::string sampler = GetSampler(instr.sampler, texture_type, false, depth_compare); - // 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 = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4.component) + ')'; if (!depth_compare) { @@ -2845,7 +2863,7 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } --shader.scope; - shader.AddLine("}"); + shader.AddLine('}'); break; } case OpCode::Id::TLD4S: { @@ -2856,6 +2874,10 @@ private: instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); + // Scope to avoid variable name overlaps. + shader.AddLine('{'); + ++shader.scope; + const bool depth_compare = instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); @@ -2863,28 +2885,33 @@ private: // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. const std::string sampler = GetSampler( instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); - std::string coord; if (!depth_compare) { - coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; + shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");"); } else { // Note: TLD4S coordinate encoding works just like TEXS's - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; + shader.AddLine( + "float op_y = " + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1) + ';'); + shader.AddLine("vec3 coords = vec3(" + op_a + ", op_y, " + op_b + ");"); } const std::string texture = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4s.component) + ')'; if (!depth_compare) { - WriteTexsInstruction(instr, coord, texture); + WriteTexsInstruction(instr, texture); } else { - WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + WriteTexsInstruction(instr, "vec4(" + texture + ')'); } + + --shader.scope; + shader.AddLine('}'); break; } case OpCode::Id::TXQ: { UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); + ++shader.scope; + shader.AddLine('{'); // 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. @@ -2892,8 +2919,14 @@ private: GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, 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); + const std::string texture = "textureSize(" + sampler + ", " + + regs.GetRegisterAsInteger(instr.gpr8) + ')'; + const std::string mip_level = "textureQueryLevels(" + sampler + ')'; + shader.AddLine("ivec2 sizes = " + texture + ';'); + regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1); break; } default: { @@ -2901,6 +2934,8 @@ private: static_cast<u32>(instr.txq.query_type.Value())); } } + --shader.scope; + shader.AddLine('}'); break; } case OpCode::Id::TMML: { diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 065b3929c..a8833c06e 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -218,14 +218,19 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { case Maxwell::Blend::Equation::Add: + case Maxwell::Blend::Equation::AddGL: return GL_FUNC_ADD; case Maxwell::Blend::Equation::Subtract: + case Maxwell::Blend::Equation::SubtractGL: return GL_FUNC_SUBTRACT; case Maxwell::Blend::Equation::ReverseSubtract: + case Maxwell::Blend::Equation::ReverseSubtractGL: return GL_FUNC_REVERSE_SUBTRACT; case Maxwell::Blend::Equation::Min: + case Maxwell::Blend::Equation::MinGL: return GL_MIN; case Maxwell::Blend::Equation::Max: + case Maxwell::Blend::Equation::MaxGL: return GL_MAX; } LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); |