summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--appveyor.yml2
-rw-r--r--src/core/hle/service/am/am.cpp202
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h47
-rw-r--r--src/video_core/engines/maxwell_3d.h40
-rw-r--r--src/video_core/engines/shader_bytecode.h17
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp52
-rw-r--r--src/yuzu/main.cpp26
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp23
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h3
13 files changed, 330 insertions, 91 deletions
diff --git a/appveyor.yml b/appveyor.yml
index 72cda26a7..4f928adb5 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -53,7 +53,7 @@ build_script:
53 # https://www.appveyor.com/docs/build-phase 53 # https://www.appveyor.com/docs/build-phase
54 msbuild msvc_build/yuzu.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 54 msbuild msvc_build/yuzu.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
55 } else { 55 } else {
56 C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -C mingw_build/ 2>&1' 56 C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -j4 -C mingw_build/ 2>&1'
57 } 57 }
58 58
59after_build: 59after_build:
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 6b1d6bf97..12954556d 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <stack>
6#include "core/file_sys/filesystem.h" 7#include "core/file_sys/filesystem.h"
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
@@ -154,7 +155,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
154 RegisterHandlers(functions); 155 RegisterHandlers(functions);
155 156
156 launchable_event = 157 launchable_event =
157 Kernel::Event::Create(Kernel::ResetType::OneShot, "ISelfController:LaunchableEvent"); 158 Kernel::Event::Create(Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
158} 159}
159 160
160void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 161void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
@@ -348,19 +349,100 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
348 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 349 NGLOG_WARNING(Service_AM, "(STUBBED) called");
349} 350}
350 351
352class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
353public:
354 explicit IStorageAccessor(std::vector<u8> buffer)
355 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
356 static const FunctionInfo functions[] = {
357 {0, &IStorageAccessor::GetSize, "GetSize"},
358 {10, &IStorageAccessor::Write, "Write"},
359 {11, &IStorageAccessor::Read, "Read"},
360 };
361 RegisterHandlers(functions);
362 }
363
364private:
365 std::vector<u8> buffer;
366
367 void GetSize(Kernel::HLERequestContext& ctx) {
368 IPC::ResponseBuilder rb{ctx, 4};
369
370 rb.Push(RESULT_SUCCESS);
371 rb.Push(static_cast<u64>(buffer.size()));
372
373 NGLOG_DEBUG(Service_AM, "called");
374 }
375
376 void Write(Kernel::HLERequestContext& ctx) {
377 IPC::RequestParser rp{ctx};
378
379 const u64 offset{rp.Pop<u64>()};
380 const std::vector<u8> data{ctx.ReadBuffer()};
381
382 ASSERT(offset + data.size() <= buffer.size());
383
384 std::memcpy(&buffer[offset], data.data(), data.size());
385
386 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
387 rb.Push(RESULT_SUCCESS);
388
389 NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
390 }
391
392 void Read(Kernel::HLERequestContext& ctx) {
393 IPC::RequestParser rp{ctx};
394
395 const u64 offset{rp.Pop<u64>()};
396 const size_t size{ctx.GetWriteBufferSize()};
397
398 ASSERT(offset + size <= buffer.size());
399
400 ctx.WriteBuffer(buffer.data() + offset, size);
401
402 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
403 rb.Push(RESULT_SUCCESS);
404
405 NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
406 }
407};
408
409class IStorage final : public ServiceFramework<IStorage> {
410public:
411 explicit IStorage(std::vector<u8> buffer)
412 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
413 static const FunctionInfo functions[] = {
414 {0, &IStorage::Open, "Open"},
415 {1, nullptr, "OpenTransferStorage"},
416 };
417 RegisterHandlers(functions);
418 }
419
420private:
421 std::vector<u8> buffer;
422
423 void Open(Kernel::HLERequestContext& ctx) {
424 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
425
426 rb.Push(RESULT_SUCCESS);
427 rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
428
429 NGLOG_DEBUG(Service_AM, "called");
430 }
431};
432
351class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 433class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
352public: 434public:
353 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { 435 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
354 static const FunctionInfo functions[] = { 436 static const FunctionInfo functions[] = {
355 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 437 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
356 {1, nullptr, "IsCompleted"}, 438 {1, nullptr, "IsCompleted"},
357 {10, nullptr, "Start"}, 439 {10, &ILibraryAppletAccessor::Start, "Start"},
358 {20, nullptr, "RequestExit"}, 440 {20, nullptr, "RequestExit"},
359 {25, nullptr, "Terminate"}, 441 {25, nullptr, "Terminate"},
360 {30, nullptr, "GetResult"}, 442 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
361 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, 443 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
362 {100, nullptr, "PushInData"}, 444 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
363 {101, nullptr, "PopOutData"}, 445 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
364 {102, nullptr, "PushExtraStorage"}, 446 {102, nullptr, "PushExtraStorage"},
365 {103, nullptr, "PushInteractiveInData"}, 447 {103, nullptr, "PushInteractiveInData"},
366 {104, nullptr, "PopInteractiveOutData"}, 448 {104, nullptr, "PopInteractiveOutData"},
@@ -388,6 +470,41 @@ private:
388 NGLOG_WARNING(Service_AM, "(STUBBED) called"); 470 NGLOG_WARNING(Service_AM, "(STUBBED) called");
389 } 471 }
390 472
473 void GetResult(Kernel::HLERequestContext& ctx) {
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(RESULT_SUCCESS);
476
477 NGLOG_WARNING(Service_AM, "(STUBBED) called");
478 }
479
480 void Start(Kernel::HLERequestContext& ctx) {
481 IPC::ResponseBuilder rb{ctx, 2};
482 rb.Push(RESULT_SUCCESS);
483
484 NGLOG_WARNING(Service_AM, "(STUBBED) called");
485 }
486
487 void PushInData(Kernel::HLERequestContext& ctx) {
488 IPC::RequestParser rp{ctx};
489 storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
490
491 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
492 rb.Push(RESULT_SUCCESS);
493
494 NGLOG_DEBUG(Service_AM, "called");
495 }
496
497 void PopOutData(Kernel::HLERequestContext& ctx) {
498 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
499 rb.Push(RESULT_SUCCESS);
500 rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
501
502 storage_stack.pop();
503
504 NGLOG_DEBUG(Service_AM, "called");
505 }
506
507 std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
391 Kernel::SharedPtr<Kernel::Event> state_changed_event; 508 Kernel::SharedPtr<Kernel::Event> state_changed_event;
392}; 509};
393 510
@@ -396,7 +513,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
396 {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, 513 {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
397 {1, nullptr, "TerminateAllLibraryApplets"}, 514 {1, nullptr, "TerminateAllLibraryApplets"},
398 {2, nullptr, "AreAnyLibraryAppletsLeft"}, 515 {2, nullptr, "AreAnyLibraryAppletsLeft"},
399 {10, nullptr, "CreateStorage"}, 516 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
400 {11, nullptr, "CreateTransferMemoryStorage"}, 517 {11, nullptr, "CreateTransferMemoryStorage"},
401 {12, nullptr, "CreateHandleStorage"}, 518 {12, nullptr, "CreateHandleStorage"},
402 }; 519 };
@@ -412,72 +529,17 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
412 NGLOG_DEBUG(Service_AM, "called"); 529 NGLOG_DEBUG(Service_AM, "called");
413} 530}
414 531
415class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 532void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
416public: 533 IPC::RequestParser rp{ctx};
417 explicit IStorageAccessor(std::vector<u8> buffer) 534 const u64 size{rp.Pop<u64>()};
418 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { 535 std::vector<u8> buffer(size);
419 static const FunctionInfo functions[] = {
420 {0, &IStorageAccessor::GetSize, "GetSize"},
421 {10, nullptr, "Write"},
422 {11, &IStorageAccessor::Read, "Read"},
423 };
424 RegisterHandlers(functions);
425 }
426
427private:
428 std::vector<u8> buffer;
429
430 void GetSize(Kernel::HLERequestContext& ctx) {
431 IPC::ResponseBuilder rb{ctx, 4};
432
433 rb.Push(RESULT_SUCCESS);
434 rb.Push(static_cast<u64>(buffer.size()));
435
436 NGLOG_DEBUG(Service_AM, "called");
437 }
438
439 void Read(Kernel::HLERequestContext& ctx) {
440 IPC::RequestParser rp{ctx};
441
442 u64 offset = rp.Pop<u64>();
443
444 const size_t size{ctx.GetWriteBufferSize()};
445
446 ASSERT(offset + size <= buffer.size());
447
448 ctx.WriteBuffer(buffer.data() + offset, size);
449
450 IPC::ResponseBuilder rb{ctx, 2};
451
452 rb.Push(RESULT_SUCCESS);
453
454 NGLOG_DEBUG(Service_AM, "called");
455 }
456};
457
458class IStorage final : public ServiceFramework<IStorage> {
459public:
460 explicit IStorage(std::vector<u8> buffer)
461 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
462 static const FunctionInfo functions[] = {
463 {0, &IStorage::Open, "Open"},
464 {1, nullptr, "OpenTransferStorage"},
465 };
466 RegisterHandlers(functions);
467 }
468
469private:
470 std::vector<u8> buffer;
471
472 void Open(Kernel::HLERequestContext& ctx) {
473 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
474 536
475 rb.Push(RESULT_SUCCESS); 537 IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)};
476 rb.PushIpcInterface<AM::IStorageAccessor>(buffer); 538 rb.Push(RESULT_SUCCESS);
539 rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
477 540
478 NGLOG_DEBUG(Service_AM, "called"); 541 NGLOG_DEBUG(Service_AM, "called, size={}", size);
479 } 542}
480};
481 543
482IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 544IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
483 static const FunctionInfo functions[] = { 545 static const FunctionInfo functions[] = {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index ff8eb14d7..301a6c798 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -121,6 +121,7 @@ public:
121 121
122private: 122private:
123 void CreateLibraryApplet(Kernel::HLERequestContext& ctx); 123 void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
124 void CreateStorage(Kernel::HLERequestContext& ctx);
124}; 125};
125 126
126class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 127class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 2ecf818f3..56b5ed60d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -26,11 +26,19 @@ public:
26private: 26private:
27 enum class IoctlCommand : u32_le { 27 enum class IoctlCommand : u32_le {
28 IocSetNVMAPfdCommand = 0x40044801, 28 IocSetNVMAPfdCommand = 0x40044801,
29 IocAllocGPFIFOCommand = 0x40084805,
29 IocSetClientDataCommand = 0x40084714, 30 IocSetClientDataCommand = 0x40084714,
30 IocGetClientDataCommand = 0x80084715, 31 IocGetClientDataCommand = 0x80084715,
31 IocZCullBind = 0xc010480b, 32 IocZCullBind = 0xc010480b,
32 IocSetErrorNotifierCommand = 0xC018480C, 33 IocSetErrorNotifierCommand = 0xC018480C,
33 IocChannelSetPriorityCommand = 0x4004480D, 34 IocChannelSetPriorityCommand = 0x4004480D,
35 IocEnableCommand = 0x0000480E,
36 IocDisableCommand = 0x0000480F,
37 IocPreemptCommand = 0x00004810,
38 IocForceResetCommand = 0x00004811,
39 IocEventIdControlCommand = 0x40084812,
40 IocGetErrorNotificationCommand = 0xC0104817,
41 IocAllocGPFIFOExCommand = 0x40204818,
34 IocAllocGPFIFOEx2Command = 0xC020481A, 42 IocAllocGPFIFOEx2Command = 0xC020481A,
35 IocAllocObjCtxCommand = 0xC0104809, 43 IocAllocObjCtxCommand = 0xC0104809,
36 IocChannelGetWaitbaseCommand = 0xC0080003, 44 IocChannelGetWaitbaseCommand = 0xC0080003,
@@ -56,6 +64,12 @@ private:
56 }; 64 };
57 static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size"); 65 static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
58 66
67 struct IoctlAllocGPFIFO {
68 u32_le num_entries;
69 u32_le flags;
70 };
71 static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
72
59 struct IoctlClientData { 73 struct IoctlClientData {
60 u64_le data; 74 u64_le data;
61 }; 75 };
@@ -76,12 +90,45 @@ private:
76 }; 90 };
77 static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size"); 91 static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
78 92
93 struct IoctlChannelSetPriority {
94 u32_le priority;
95 };
96 static_assert(sizeof(IoctlChannelSetPriority) == 4,
97 "IoctlChannelSetPriority is incorrect size");
98
99 struct IoctlEventIdControl {
100 u32_le cmd; // 0=disable, 1=enable, 2=clear
101 u32_le id;
102 };
103 static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
104
105 struct IoctlGetErrorNotification {
106 u64_le timestamp;
107 u32_le info32;
108 u16_le info16;
109 u16_le status; // always 0xFFFF
110 };
111 static_assert(sizeof(IoctlGetErrorNotification) == 16,
112 "IoctlGetErrorNotification is incorrect size");
113
79 struct IoctlFence { 114 struct IoctlFence {
80 u32_le id; 115 u32_le id;
81 u32_le value; 116 u32_le value;
82 }; 117 };
83 static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size"); 118 static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
84 119
120 struct IoctlAllocGpfifoEx {
121 u32_le num_entries;
122 u32_le flags;
123 u32_le unk0;
124 u32_le unk1;
125 u32_le unk2;
126 u32_le unk3;
127 u32_le unk4;
128 u32_le unk5;
129 };
130 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
131
85 struct IoctlAllocGpfifoEx2 { 132 struct IoctlAllocGpfifoEx2 {
86 u32_le num_entries; // in 133 u32_le num_entries; // in
87 u32_le flags; // in 134 u32_le flags; // in
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 5cf62fb01..245410c95 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -354,10 +354,35 @@ public:
354 f32 scale_x; 354 f32 scale_x;
355 f32 scale_y; 355 f32 scale_y;
356 f32 scale_z; 356 f32 scale_z;
357 u32 translate_x; 357 f32 translate_x;
358 u32 translate_y; 358 f32 translate_y;
359 u32 translate_z; 359 f32 translate_z;
360 INSERT_PADDING_WORDS(2); 360 INSERT_PADDING_WORDS(2);
361
362 MathUtil::Rectangle<s32> GetRect() const {
363 return {
364 GetX(), // left
365 GetY() + GetHeight(), // top
366 GetX() + GetWidth(), // right
367 GetY() // bottom
368 };
369 };
370
371 s32 GetX() const {
372 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
373 }
374
375 s32 GetY() const {
376 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
377 }
378
379 s32 GetWidth() const {
380 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
381 }
382
383 s32 GetHeight() const {
384 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
385 }
361 } viewport_transform[NumViewports]; 386 } viewport_transform[NumViewports];
362 387
363 struct { 388 struct {
@@ -371,15 +396,6 @@ public:
371 }; 396 };
372 float depth_range_near; 397 float depth_range_near;
373 float depth_range_far; 398 float depth_range_far;
374
375 MathUtil::Rectangle<s32> GetRect() const {
376 return {
377 static_cast<s32>(x), // left
378 static_cast<s32>(y + height), // top
379 static_cast<s32>(x + width), // right
380 static_cast<s32>(y) // bottom
381 };
382 };
383 } viewport[NumViewports]; 399 } viewport[NumViewports];
384 400
385 INSERT_PADDING_WORDS(0x1D); 401 INSERT_PADDING_WORDS(0x1D);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 83f7cc3a9..3add56155 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -255,9 +255,9 @@ union Instruction {
255 BitField<44, 1, u64> abs_b; 255 BitField<44, 1, u64> abs_b;
256 BitField<45, 2, PredOperation> op; 256 BitField<45, 2, PredOperation> op;
257 BitField<48, 4, PredCondition> cond; 257 BitField<48, 4, PredCondition> cond;
258 BitField<52, 1, u64> bf;
258 BitField<53, 1, u64> neg_b; 259 BitField<53, 1, u64> neg_b;
259 BitField<54, 1, u64> abs_a; 260 BitField<54, 1, u64> abs_a;
260 BitField<52, 1, u64> bf;
261 BitField<55, 1, u64> ftz; 261 BitField<55, 1, u64> ftz;
262 BitField<56, 1, u64> neg_imm; 262 BitField<56, 1, u64> neg_imm;
263 } fset; 263 } fset;
@@ -298,6 +298,19 @@ union Instruction {
298 } 298 }
299 } texs; 299 } texs;
300 300
301 union {
302 BitField<20, 5, u64> target;
303 BitField<5, 1, u64> constant_buffer;
304
305 s32 GetBranchTarget() const {
306 // Sign extend the branch target offset
307 u32 mask = 1U << (5 - 1);
308 u32 value = static_cast<u32>(target);
309 // The branch offset is relative to the next instruction, so add 1 to it.
310 return static_cast<s32>((value ^ mask) - mask) + 1;
311 }
312 } bra;
313
301 BitField<61, 1, u64> is_b_imm; 314 BitField<61, 1, u64> is_b_imm;
302 BitField<60, 1, u64> is_b_gpr; 315 BitField<60, 1, u64> is_b_gpr;
303 BitField<59, 1, u64> is_c_gpr; 316 BitField<59, 1, u64> is_c_gpr;
@@ -316,6 +329,7 @@ class OpCode {
316public: 329public:
317 enum class Id { 330 enum class Id {
318 KIL, 331 KIL,
332 BRA,
319 LD_A, 333 LD_A,
320 ST_A, 334 ST_A,
321 TEX, 335 TEX,
@@ -480,6 +494,7 @@ private:
480 std::vector<Matcher> table = { 494 std::vector<Matcher> table = {
481#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) 495#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
482 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 496 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
497 INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
483 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 498 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
484 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 499 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
485 INST("1100000000111---", Id::TEX, Type::Memory, "TEX"), 500 INST("1100000000111---", Id::TEX, Type::Memory, "TEX"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 35c1b1890..0a33868b7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -298,7 +298,7 @@ void RasterizerOpenGL::DrawArrays() {
298 const bool has_stencil = false; 298 const bool has_stencil = false;
299 const bool using_color_fb = true; 299 const bool using_color_fb = true;
300 const bool using_depth_fb = false; 300 const bool using_depth_fb = false;
301 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()}; 301 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
302 302
303 const bool write_color_fb = 303 const bool write_color_fb =
304 state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || 304 state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
@@ -702,7 +702,7 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
702 702
703void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) { 703void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) {
704 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; 704 const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
705 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()}; 705 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
706 706
707 state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale; 707 state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale;
708 state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale; 708 state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 65d643447..d6048f639 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -933,7 +933,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
933 // Use GetSurfaceSubRect instead 933 // Use GetSurfaceSubRect instead
934 ASSERT(params.width == params.stride); 934 ASSERT(params.width == params.stride);
935 935
936 ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0)); 936 ASSERT(!params.is_tiled ||
937 (params.GetActualWidth() % 8 == 0 && params.GetActualHeight() % 8 == 0));
937 938
938 // Check for an exact match in existing surfaces 939 // Check for an exact match in existing surfaces
939 Surface surface = 940 Surface surface =
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 618c603c2..8c263f15f 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -88,6 +88,20 @@ private:
88 return *subroutines.insert(std::move(subroutine)).first; 88 return *subroutines.insert(std::move(subroutine)).first;
89 } 89 }
90 90
91 /// Merges exit method of two parallel branches.
92 static ExitMethod ParallelExit(ExitMethod a, ExitMethod b) {
93 if (a == ExitMethod::Undetermined) {
94 return b;
95 }
96 if (b == ExitMethod::Undetermined) {
97 return a;
98 }
99 if (a == b) {
100 return a;
101 }
102 return ExitMethod::Conditional;
103 }
104
91 /// Scans a range of code for labels and determines the exit method. 105 /// Scans a range of code for labels and determines the exit method.
92 ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) { 106 ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels) {
93 auto [iter, inserted] = 107 auto [iter, inserted] =
@@ -97,11 +111,19 @@ private:
97 return exit_method; 111 return exit_method;
98 112
99 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 113 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
100 if (const auto opcode = OpCode::Decode({program_code[offset]})) { 114 const Instruction instr = {program_code[offset]};
115 if (const auto opcode = OpCode::Decode(instr)) {
101 switch (opcode->GetId()) { 116 switch (opcode->GetId()) {
102 case OpCode::Id::EXIT: { 117 case OpCode::Id::EXIT: {
103 return exit_method = ExitMethod::AlwaysEnd; 118 return exit_method = ExitMethod::AlwaysEnd;
104 } 119 }
120 case OpCode::Id::BRA: {
121 u32 target = offset + instr.bra.GetBranchTarget();
122 labels.insert(target);
123 ExitMethod no_jmp = Scan(offset + 1, end, labels);
124 ExitMethod jmp = Scan(target, end, labels);
125 return exit_method = ParallelExit(no_jmp, jmp);
126 }
105 } 127 }
106 } 128 }
107 } 129 }
@@ -860,8 +882,7 @@ private:
860 ASSERT_MSG(!instr.conversion.saturate_a, "Unimplemented"); 882 ASSERT_MSG(!instr.conversion.saturate_a, "Unimplemented");
861 883
862 switch (opcode->GetId()) { 884 switch (opcode->GetId()) {
863 case OpCode::Id::I2I_R: 885 case OpCode::Id::I2I_R: {
864 case OpCode::Id::I2F_R: {
865 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 886 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
866 887
867 std::string op_a = 888 std::string op_a =
@@ -874,6 +895,17 @@ private:
874 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1); 895 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_signed, 0, op_a, 1, 1);
875 break; 896 break;
876 } 897 }
898 case OpCode::Id::I2F_R: {
899 std::string op_a =
900 regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_signed);
901
902 if (instr.conversion.abs_a) {
903 op_a = "abs(" + op_a + ')';
904 }
905
906 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
907 break;
908 }
877 case OpCode::Id::F2F_R: { 909 case OpCode::Id::F2F_R: {
878 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); 910 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
879 911
@@ -1103,7 +1135,12 @@ private:
1103 std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " + 1135 std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " +
1104 combiner + " (" + second_pred + "))"; 1136 combiner + " (" + second_pred + "))";
1105 1137
1106 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1); 1138 if (instr.fset.bf) {
1139 regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
1140 } else {
1141 regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
1142 1);
1143 }
1107 break; 1144 break;
1108 } 1145 }
1109 default: { 1146 default: {
@@ -1128,6 +1165,13 @@ private:
1128 shader.AddLine("discard;"); 1165 shader.AddLine("discard;");
1129 break; 1166 break;
1130 } 1167 }
1168 case OpCode::Id::BRA: {
1169 ASSERT_MSG(instr.bra.constant_buffer == 0,
1170 "BRA with constant buffers are not implemented");
1171 u32 target = offset + instr.bra.GetBranchTarget();
1172 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
1173 break;
1174 }
1131 case OpCode::Id::IPA: { 1175 case OpCode::Id::IPA: {
1132 const auto& attribute = instr.attribute.fmt28; 1176 const auto& attribute = instr.attribute.fmt28;
1133 regs.SetRegisterToInputAttibute(instr.gpr0, attribute.element, attribute.index); 1177 regs.SetRegisterToInputAttibute(instr.gpr0, attribute.element, attribute.index);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a5d7807e2..3038bd6da 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -335,6 +335,24 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
335 } 335 }
336} 336}
337 337
338bool GMainWindow::SupportsRequiredGLExtensions() {
339 QStringList unsupported_ext;
340
341 if (!GLAD_GL_ARB_program_interface_query)
342 unsupported_ext.append("ARB_program_interface_query");
343 if (!GLAD_GL_ARB_separate_shader_objects)
344 unsupported_ext.append("ARB_separate_shader_objects");
345 if (!GLAD_GL_ARB_shader_storage_buffer_object)
346 unsupported_ext.append("ARB_shader_storage_buffer_object");
347 if (!GLAD_GL_ARB_vertex_attrib_binding)
348 unsupported_ext.append("ARB_vertex_attrib_binding");
349
350 for (const QString& ext : unsupported_ext)
351 NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
352
353 return unsupported_ext.empty();
354}
355
338bool GMainWindow::LoadROM(const QString& filename) { 356bool GMainWindow::LoadROM(const QString& filename) {
339 // Shutdown previous session if the emu thread is still active... 357 // Shutdown previous session if the emu thread is still active...
340 if (emu_thread != nullptr) 358 if (emu_thread != nullptr)
@@ -350,6 +368,14 @@ bool GMainWindow::LoadROM(const QString& filename) {
350 return false; 368 return false;
351 } 369 }
352 370
371 if (!SupportsRequiredGLExtensions()) {
372 QMessageBox::critical(
373 this, tr("Error while initializing OpenGL Core!"),
374 tr("Your GPU may not support one or more required OpenGL extensions. Please "
375 "ensure you have the latest graphics driver. See the log for more details."));
376 return false;
377 }
378
353 Core::System& system{Core::System::GetInstance()}; 379 Core::System& system{Core::System::GetInstance()};
354 380
355 system.SetGPUDebugContext(debug_context); 381 system.SetGPUDebugContext(debug_context);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 20ff65314..ac3024d8a 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -79,6 +79,7 @@ private:
79 void ConnectWidgetEvents(); 79 void ConnectWidgetEvents();
80 void ConnectMenuEvents(); 80 void ConnectMenuEvents();
81 81
82 bool SupportsRequiredGLExtensions();
82 bool LoadROM(const QString& filename); 83 bool LoadROM(const QString& filename);
83 void BootGame(const QString& filename); 84 void BootGame(const QString& filename);
84 void ShutdownGame(); 85 void ShutdownGame();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e21de6f21..cfd8eb7e6 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -78,6 +78,24 @@ void EmuWindow_SDL2::Fullscreen() {
78 SDL_MaximizeWindow(render_window); 78 SDL_MaximizeWindow(render_window);
79} 79}
80 80
81bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
82 std::vector<std::string> unsupported_ext;
83
84 if (!GLAD_GL_ARB_program_interface_query)
85 unsupported_ext.push_back("ARB_program_interface_query");
86 if (!GLAD_GL_ARB_separate_shader_objects)
87 unsupported_ext.push_back("ARB_separate_shader_objects");
88 if (!GLAD_GL_ARB_shader_storage_buffer_object)
89 unsupported_ext.push_back("ARB_shader_storage_buffer_object");
90 if (!GLAD_GL_ARB_vertex_attrib_binding)
91 unsupported_ext.push_back("ARB_vertex_attrib_binding");
92
93 for (const std::string& ext : unsupported_ext)
94 NGLOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
95
96 return unsupported_ext.empty();
97}
98
81EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { 99EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
82 InputCommon::Init(); 100 InputCommon::Init();
83 101
@@ -128,6 +146,11 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
128 exit(1); 146 exit(1);
129 } 147 }
130 148
149 if (!SupportsRequiredGLExtensions()) {
150 NGLOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
151 exit(1);
152 }
153
131 OnResize(); 154 OnResize();
132 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 155 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
133 SDL_PumpEvents(); 156 SDL_PumpEvents();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 7d5cfffb6..1d835c3c6 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -46,6 +46,9 @@ private:
46 /// Called when user passes the fullscreen parameter flag 46 /// Called when user passes the fullscreen parameter flag
47 void Fullscreen(); 47 void Fullscreen();
48 48
49 /// Whether the GPU and driver supports the OpenGL extension required
50 bool SupportsRequiredGLExtensions();
51
49 /// Called when a configuration change affects the minimal size of the window 52 /// Called when a configuration change affects the minimal size of the window
50 void OnMinimalClientAreaChangeRequest( 53 void OnMinimalClientAreaChangeRequest(
51 const std::pair<unsigned, unsigned>& minimal_size) override; 54 const std::pair<unsigned, unsigned>& minimal_size) override;