summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/cubeb_sink.cpp20
-rw-r--r--src/common/math_util.h16
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/frontend/framebuffer_layout.cpp4
-rw-r--r--src/core/frontend/framebuffer_layout.h16
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/dirty_flags.cpp46
-rw-r--r--src/video_core/dirty_flags.h51
-rw-r--r--src/video_core/dma_pusher.cpp2
-rw-r--r--src/video_core/engines/kepler_compute.cpp2
-rw-r--r--src/video_core/engines/kepler_memory.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp187
-rw-r--r--src/video_core/engines/maxwell_3d.h150
-rw-r--r--src/video_core/engines/maxwell_dma.cpp2
-rw-r--r--src/video_core/rasterizer_interface.h3
-rw-r--r--src/video_core/renderer_opengl/gl_framebuffer_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_framebuffer_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp868
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h45
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp28
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h25
-rw-r--r--src/video_core/renderer_opengl/gl_sampler_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp43
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h39
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp569
-rw-r--r--src/video_core/renderer_opengl/gl_state.h251
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp238
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h204
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp47
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h10
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h14
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp205
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h14
-rw-r--r--src/video_core/renderer_opengl/utils.cpp13
-rw-r--r--src/video_core/renderer_opengl/utils.h9
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp15
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h8
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp14
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.h4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp8
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp76
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h17
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp21
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h42
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp101
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h79
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp1
-rw-r--r--src/video_core/texture_cache/texture_cache.h18
-rw-r--r--src/video_core/textures/texture.h26
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics.ui24
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp48
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h30
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui111
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h4
-rw-r--r--src/yuzu_tester/config.cpp2
-rw-r--r--src/yuzu_tester/default_ini.h4
71 files changed, 1891 insertions, 1960 deletions
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 7047ed9cf..c4e0e30fe 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -8,6 +8,7 @@
8#include "audio_core/cubeb_sink.h" 8#include "audio_core/cubeb_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h" 10#include "audio_core/time_stretch.h"
11#include "common/assert.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "common/ring_buffer.h" 13#include "common/ring_buffer.h"
13#include "core/settings.h" 14#include "core/settings.h"
@@ -65,12 +66,25 @@ public:
65 void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override { 66 void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
66 if (source_num_channels > num_channels) { 67 if (source_num_channels > num_channels) {
67 // Downsample 6 channels to 2 68 // Downsample 6 channels to 2
69 ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
70
68 std::vector<s16> buf; 71 std::vector<s16> buf;
69 buf.reserve(samples.size() * num_channels / source_num_channels); 72 buf.reserve(samples.size() * num_channels / source_num_channels);
70 for (std::size_t i = 0; i < samples.size(); i += source_num_channels) { 73 for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
71 for (std::size_t ch = 0; ch < num_channels; ch++) { 74 // Downmixing implementation taken from the ATSC standard
72 buf.push_back(samples[i + ch]); 75 const s16 left{samples[i + 0]};
73 } 76 const s16 right{samples[i + 1]};
77 const s16 center{samples[i + 2]};
78 const s16 surround_left{samples[i + 4]};
79 const s16 surround_right{samples[i + 5]};
80 // Not used in the ATSC reference implementation
81 [[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] };
82
83 constexpr s32 clev{707}; // center mixing level coefficient
84 constexpr s32 slev{707}; // surround mixing level coefficient
85
86 buf.push_back(left + (clev * center / 1000) + (slev * surround_left / 1000));
87 buf.push_back(right + (clev * center / 1000) + (slev * surround_right / 1000));
74 } 88 }
75 queue.Push(buf); 89 queue.Push(buf);
76 return; 90 return;
diff --git a/src/common/math_util.h b/src/common/math_util.h
index d6c35ee89..83ef0201f 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -24,17 +24,29 @@ struct Rectangle {
24 : left(left), top(top), right(right), bottom(bottom) {} 24 : left(left), top(top), right(right), bottom(bottom) {}
25 25
26 T GetWidth() const { 26 T GetWidth() const {
27 return std::abs(static_cast<std::make_signed_t<T>>(right - left)); 27 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left);
29 } else {
30 return std::abs(static_cast<std::make_signed_t<T>>(right - left));
31 }
28 } 32 }
33
29 T GetHeight() const { 34 T GetHeight() const {
30 return std::abs(static_cast<std::make_signed_t<T>>(bottom - top)); 35 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top);
37 } else {
38 return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
39 }
31 } 40 }
41
32 Rectangle<T> TranslateX(const T x) const { 42 Rectangle<T> TranslateX(const T x) const {
33 return Rectangle{left + x, top, right + x, bottom}; 43 return Rectangle{left + x, top, right + x, bottom};
34 } 44 }
45
35 Rectangle<T> TranslateY(const T y) const { 46 Rectangle<T> TranslateY(const T y) const {
36 return Rectangle{left, top + y, right, bottom + y}; 47 return Rectangle{left, top + y, right, bottom + y};
37 } 48 }
49
38 Rectangle<T> Scale(const float s) const { 50 Rectangle<T> Scale(const float s) const {
39 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s), 51 return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
40 static_cast<T>(top + GetHeight() * s)}; 52 static_cast<T>(top + GetHeight() * s)};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index a82faf127..218508126 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -174,6 +174,7 @@ struct System::Impl {
174 } 174 }
175 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); 175 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
176 gpu_core = VideoCore::CreateGPU(system); 176 gpu_core = VideoCore::CreateGPU(system);
177 renderer->Rasterizer().SetupDirtyFlags();
177 178
178 is_powered_on = true; 179 is_powered_on = true;
179 exit_lock = false; 180 exit_lock = false;
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 2dc795d56..68a0e0906 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -48,8 +48,8 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
48 u32 width, height; 48 u32 width, height;
49 49
50 if (Settings::values.use_docked_mode) { 50 if (Settings::values.use_docked_mode) {
51 width = ScreenDocked::WidthDocked * res_scale; 51 width = ScreenDocked::Width * res_scale;
52 height = ScreenDocked::HeightDocked * res_scale; 52 height = ScreenDocked::Height * res_scale;
53 } else { 53 } else {
54 width = ScreenUndocked::Width * res_scale; 54 width = ScreenUndocked::Width * res_scale;
55 height = ScreenUndocked::Height * res_scale; 55 height = ScreenUndocked::Height * res_scale;
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index e9d0a40d3..15ecfb13d 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -8,15 +8,15 @@
8 8
9namespace Layout { 9namespace Layout {
10 10
11enum ScreenUndocked : u32 { 11namespace ScreenUndocked {
12 Width = 1280, 12constexpr u32 Width = 1280;
13 Height = 720, 13constexpr u32 Height = 720;
14}; 14} // namespace ScreenUndocked
15 15
16enum ScreenDocked : u32 { 16namespace ScreenDocked {
17 WidthDocked = 1920, 17constexpr u32 Width = 1920;
18 HeightDocked = 1080, 18constexpr u32 Height = 1080;
19}; 19} // namespace ScreenDocked
20 20
21enum class AspectRatio { 21enum class AspectRatio {
22 Default, 22 Default,
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 134152210..437bc5dee 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -191,8 +191,6 @@ void NVFlinger::Compose() {
191 // Search for a queued buffer and acquire it 191 // Search for a queued buffer and acquire it
192 auto buffer = buffer_queue.AcquireBuffer(); 192 auto buffer = buffer_queue.AcquireBuffer();
193 193
194 MicroProfileFlip();
195
196 if (!buffer) { 194 if (!buffer) {
197 continue; 195 continue;
198 } 196 }
@@ -206,6 +204,8 @@ void NVFlinger::Compose() {
206 gpu.WaitFence(fence.id, fence.value); 204 gpu.WaitFence(fence.id, fence.value);
207 } 205 }
208 206
207 MicroProfileFlip();
208
209 // Now send the buffer to the GPU for drawing. 209 // Now send the buffer to the GPU for drawing.
210 // TODO(Subv): Support more than just disp0. The display device selection is probably based 210 // TODO(Subv): Support more than just disp0. The display device selection is probably based
211 // on which display we're drawing (Default, Internal, External, etc) 211 // on which display we're drawing (Default, Internal, External, etc)
diff --git a/src/core/settings.h b/src/core/settings.h
index 15b691342..cb5979e6f 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -430,6 +430,7 @@ struct Values {
430 430
431 float resolution_factor; 431 float resolution_factor;
432 int aspect_ratio; 432 int aspect_ratio;
433 int max_anisotropy;
433 bool use_frame_limit; 434 bool use_frame_limit;
434 u16 frame_limit; 435 u16 frame_limit;
435 bool use_disk_shader_cache; 436 bool use_disk_shader_cache;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 4b0c6346f..14f3b4569 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -2,6 +2,8 @@ add_library(video_core STATIC
2 buffer_cache/buffer_block.h 2 buffer_cache/buffer_block.h
3 buffer_cache/buffer_cache.h 3 buffer_cache/buffer_cache.h
4 buffer_cache/map_interval.h 4 buffer_cache/map_interval.h
5 dirty_flags.cpp
6 dirty_flags.h
5 dma_pusher.cpp 7 dma_pusher.cpp
6 dma_pusher.h 8 dma_pusher.h
7 engines/const_buffer_engine_interface.h 9 engines/const_buffer_engine_interface.h
@@ -69,8 +71,8 @@ add_library(video_core STATIC
69 renderer_opengl/gl_shader_manager.h 71 renderer_opengl/gl_shader_manager.h
70 renderer_opengl/gl_shader_util.cpp 72 renderer_opengl/gl_shader_util.cpp
71 renderer_opengl/gl_shader_util.h 73 renderer_opengl/gl_shader_util.h
72 renderer_opengl/gl_state.cpp 74 renderer_opengl/gl_state_tracker.cpp
73 renderer_opengl/gl_state.h 75 renderer_opengl/gl_state_tracker.h
74 renderer_opengl/gl_stream_buffer.cpp 76 renderer_opengl/gl_stream_buffer.cpp
75 renderer_opengl/gl_stream_buffer.h 77 renderer_opengl/gl_stream_buffer.h
76 renderer_opengl/gl_texture_cache.cpp 78 renderer_opengl/gl_texture_cache.cpp
@@ -198,6 +200,8 @@ if (ENABLE_VULKAN)
198 renderer_vulkan/vk_shader_util.h 200 renderer_vulkan/vk_shader_util.h
199 renderer_vulkan/vk_staging_buffer_pool.cpp 201 renderer_vulkan/vk_staging_buffer_pool.cpp
200 renderer_vulkan/vk_staging_buffer_pool.h 202 renderer_vulkan/vk_staging_buffer_pool.h
203 renderer_vulkan/vk_state_tracker.cpp
204 renderer_vulkan/vk_state_tracker.h
201 renderer_vulkan/vk_stream_buffer.cpp 205 renderer_vulkan/vk_stream_buffer.cpp
202 renderer_vulkan/vk_stream_buffer.h 206 renderer_vulkan/vk_stream_buffer.h
203 renderer_vulkan/vk_swapchain.cpp 207 renderer_vulkan/vk_swapchain.cpp
diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp
new file mode 100644
index 000000000..4429f3405
--- /dev/null
+++ b/src/video_core/dirty_flags.cpp
@@ -0,0 +1,46 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstddef>
7
8#include "common/common_types.h"
9#include "video_core/dirty_flags.h"
10
11#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
12#define NUM(field_name) (sizeof(::Tegra::Engines::Maxwell3D::Regs::field_name) / sizeof(u32))
13
14namespace VideoCommon::Dirty {
15
16using Tegra::Engines::Maxwell3D;
17
18void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store) {
19 store[RenderTargets] = true;
20 store[ZetaBuffer] = true;
21 for (std::size_t i = 0; i < Maxwell3D::Regs::NumRenderTargets; ++i) {
22 store[ColorBuffer0 + i] = true;
23 }
24}
25
26void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
27 static constexpr std::size_t num_per_rt = NUM(rt[0]);
28 static constexpr std::size_t begin = OFF(rt);
29 static constexpr std::size_t num = num_per_rt * Maxwell3D::Regs::NumRenderTargets;
30 for (std::size_t rt = 0; rt < Maxwell3D::Regs::NumRenderTargets; ++rt) {
31 FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
32 }
33 FillBlock(tables[1], begin, num, RenderTargets);
34
35 static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
36 for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
37 const u8 flag = zeta_flags[i];
38 auto& table = tables[i];
39 table[OFF(zeta_enable)] = flag;
40 table[OFF(zeta_width)] = flag;
41 table[OFF(zeta_height)] = flag;
42 FillBlock(table, OFF(zeta), NUM(zeta), flag);
43 }
44}
45
46} // namespace VideoCommon::Dirty
diff --git a/src/video_core/dirty_flags.h b/src/video_core/dirty_flags.h
new file mode 100644
index 000000000..0dbafd3ef
--- /dev/null
+++ b/src/video_core/dirty_flags.h
@@ -0,0 +1,51 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <cstddef>
9#include <iterator>
10
11#include "common/common_types.h"
12#include "video_core/engines/maxwell_3d.h"
13
14namespace VideoCommon::Dirty {
15
16enum : u8 {
17 NullEntry = 0,
18
19 RenderTargets,
20 ColorBuffer0,
21 ColorBuffer1,
22 ColorBuffer2,
23 ColorBuffer3,
24 ColorBuffer4,
25 ColorBuffer5,
26 ColorBuffer6,
27 ColorBuffer7,
28 ZetaBuffer,
29
30 LastCommonEntry,
31};
32
33template <typename Integer>
34void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
35 std::size_t num, Integer dirty_index) {
36 const auto it = std::begin(table) + begin;
37 std::fill(it, it + num, static_cast<u8>(dirty_index));
38}
39
40template <typename Integer1, typename Integer2>
41void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
42 std::size_t num, Integer1 index_a, Integer2 index_b) {
43 FillBlock(tables[0], begin, num, index_a);
44 FillBlock(tables[1], begin, num, index_b);
45}
46
47void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store);
48
49void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
50
51} // namespace VideoCommon::Dirty
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 0094fd715..713c14182 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -22,7 +22,7 @@ void DmaPusher::DispatchCalls() {
22 MICROPROFILE_SCOPE(DispatchCalls); 22 MICROPROFILE_SCOPE(DispatchCalls);
23 23
24 // On entering GPU code, assume all memory may be touched by the ARM core. 24 // On entering GPU code, assume all memory may be touched by the ARM core.
25 gpu.Maxwell3D().dirty.OnMemoryWrite(); 25 gpu.Maxwell3D().OnMemoryWrite();
26 26
27 dma_pushbuffer_subindex = 0; 27 dma_pushbuffer_subindex = 0;
28 28
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 4b824aa4e..ae52afa79 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -39,7 +39,7 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
39 const bool is_last_call = method_call.IsLastCall(); 39 const bool is_last_call = method_call.IsLastCall();
40 upload_state.ProcessData(method_call.argument, is_last_call); 40 upload_state.ProcessData(method_call.argument, is_last_call);
41 if (is_last_call) { 41 if (is_last_call) {
42 system.GPU().Maxwell3D().dirty.OnMemoryWrite(); 42 system.GPU().Maxwell3D().OnMemoryWrite();
43 } 43 }
44 break; 44 break;
45 } 45 }
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index fa4a7c5c1..597872e43 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -34,7 +34,7 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
34 const bool is_last_call = method_call.IsLastCall(); 34 const bool is_last_call = method_call.IsLastCall();
35 upload_state.ProcessData(method_call.argument, is_last_call); 35 upload_state.ProcessData(method_call.argument, is_last_call);
36 if (is_last_call) { 36 if (is_last_call) {
37 system.GPU().Maxwell3D().dirty.OnMemoryWrite(); 37 system.GPU().Maxwell3D().OnMemoryWrite();
38 } 38 }
39 break; 39 break;
40 } 40 }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index b28de1092..89050361e 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -26,7 +26,8 @@ Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& raste
26 MemoryManager& memory_manager) 26 MemoryManager& memory_manager)
27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, 27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
28 macro_interpreter{*this}, upload_state{memory_manager, regs.upload} { 28 macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
29 InitDirtySettings(); 29 dirty.flags.flip();
30
30 InitializeRegisterDefaults(); 31 InitializeRegisterDefaults();
31} 32}
32 33
@@ -75,8 +76,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
75 regs.stencil_back_mask = 0xFFFFFFFF; 76 regs.stencil_back_mask = 0xFFFFFFFF;
76 77
77 regs.depth_test_func = Regs::ComparisonOp::Always; 78 regs.depth_test_func = Regs::ComparisonOp::Always;
78 regs.cull.front_face = Regs::Cull::FrontFace::CounterClockWise; 79 regs.front_face = Regs::FrontFace::CounterClockWise;
79 regs.cull.cull_face = Regs::Cull::CullFace::Back; 80 regs.cull_face = Regs::CullFace::Back;
80 81
81 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a 82 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
82 // register carrying a default value. Assume it's OpenGL's default (1). 83 // register carrying a default value. Assume it's OpenGL's default (1).
@@ -95,7 +96,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
95 regs.rasterize_enable = 1; 96 regs.rasterize_enable = 1;
96 regs.rt_separate_frag_data = 1; 97 regs.rt_separate_frag_data = 1;
97 regs.framebuffer_srgb = 1; 98 regs.framebuffer_srgb = 1;
98 regs.cull.front_face = Maxwell3D::Regs::Cull::FrontFace::ClockWise; 99 regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
99 100
100 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true; 101 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
101 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true; 102 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
@@ -103,164 +104,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
103 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; 104 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
104} 105}
105 106
106#define DIRTY_REGS_POS(field_name) static_cast<u8>(offsetof(Maxwell3D::DirtyRegs, field_name))
107
108void Maxwell3D::InitDirtySettings() {
109 const auto set_block = [this](std::size_t start, std::size_t range, u8 position) {
110 const auto start_itr = dirty_pointers.begin() + start;
111 const auto end_itr = start_itr + range;
112 std::fill(start_itr, end_itr, position);
113 };
114 dirty.regs.fill(true);
115
116 // Init Render Targets
117 constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
118 constexpr u32 rt_start_reg = MAXWELL3D_REG_INDEX(rt);
119 constexpr u32 rt_end_reg = rt_start_reg + registers_per_rt * 8;
120 u8 rt_dirty_reg = DIRTY_REGS_POS(render_target);
121 for (u32 rt_reg = rt_start_reg; rt_reg < rt_end_reg; rt_reg += registers_per_rt) {
122 set_block(rt_reg, registers_per_rt, rt_dirty_reg);
123 ++rt_dirty_reg;
124 }
125 constexpr u32 depth_buffer_flag = DIRTY_REGS_POS(depth_buffer);
126 dirty_pointers[MAXWELL3D_REG_INDEX(zeta_enable)] = depth_buffer_flag;
127 dirty_pointers[MAXWELL3D_REG_INDEX(zeta_width)] = depth_buffer_flag;
128 dirty_pointers[MAXWELL3D_REG_INDEX(zeta_height)] = depth_buffer_flag;
129 constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
130 constexpr u32 zeta_reg = MAXWELL3D_REG_INDEX(zeta);
131 set_block(zeta_reg, registers_in_zeta, depth_buffer_flag);
132
133 // Init Vertex Arrays
134 constexpr u32 vertex_array_start = MAXWELL3D_REG_INDEX(vertex_array);
135 constexpr u32 vertex_array_size = sizeof(regs.vertex_array[0]) / sizeof(u32);
136 constexpr u32 vertex_array_end = vertex_array_start + vertex_array_size * Regs::NumVertexArrays;
137 u8 va_dirty_reg = DIRTY_REGS_POS(vertex_array);
138 u8 vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
139 for (u32 vertex_reg = vertex_array_start; vertex_reg < vertex_array_end;
140 vertex_reg += vertex_array_size) {
141 set_block(vertex_reg, 3, va_dirty_reg);
142 // The divisor concerns vertex array instances
143 dirty_pointers[static_cast<std::size_t>(vertex_reg) + 3] = vi_dirty_reg;
144 ++va_dirty_reg;
145 ++vi_dirty_reg;
146 }
147 constexpr u32 vertex_limit_start = MAXWELL3D_REG_INDEX(vertex_array_limit);
148 constexpr u32 vertex_limit_size = sizeof(regs.vertex_array_limit[0]) / sizeof(u32);
149 constexpr u32 vertex_limit_end = vertex_limit_start + vertex_limit_size * Regs::NumVertexArrays;
150 va_dirty_reg = DIRTY_REGS_POS(vertex_array);
151 for (u32 vertex_reg = vertex_limit_start; vertex_reg < vertex_limit_end;
152 vertex_reg += vertex_limit_size) {
153 set_block(vertex_reg, vertex_limit_size, va_dirty_reg);
154 va_dirty_reg++;
155 }
156 constexpr u32 vertex_instance_start = MAXWELL3D_REG_INDEX(instanced_arrays);
157 constexpr u32 vertex_instance_size =
158 sizeof(regs.instanced_arrays.is_instanced[0]) / sizeof(u32);
159 constexpr u32 vertex_instance_end =
160 vertex_instance_start + vertex_instance_size * Regs::NumVertexArrays;
161 vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
162 for (u32 vertex_reg = vertex_instance_start; vertex_reg < vertex_instance_end;
163 vertex_reg += vertex_instance_size) {
164 set_block(vertex_reg, vertex_instance_size, vi_dirty_reg);
165 vi_dirty_reg++;
166 }
167 set_block(MAXWELL3D_REG_INDEX(vertex_attrib_format), regs.vertex_attrib_format.size(),
168 DIRTY_REGS_POS(vertex_attrib_format));
169
170 // Init Shaders
171 constexpr u32 shader_registers_count =
172 sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
173 set_block(MAXWELL3D_REG_INDEX(shader_config[0]), shader_registers_count,
174 DIRTY_REGS_POS(shaders));
175
176 // State
177
178 // Viewport
179 constexpr u8 viewport_dirty_reg = DIRTY_REGS_POS(viewport);
180 constexpr u32 viewport_start = MAXWELL3D_REG_INDEX(viewports);
181 constexpr u32 viewport_size = sizeof(regs.viewports) / sizeof(u32);
182 set_block(viewport_start, viewport_size, viewport_dirty_reg);
183 constexpr u32 view_volume_start = MAXWELL3D_REG_INDEX(view_volume_clip_control);
184 constexpr u32 view_volume_size = sizeof(regs.view_volume_clip_control) / sizeof(u32);
185 set_block(view_volume_start, view_volume_size, viewport_dirty_reg);
186
187 // Viewport transformation
188 constexpr u32 viewport_trans_start = MAXWELL3D_REG_INDEX(viewport_transform);
189 constexpr u32 viewport_trans_size = sizeof(regs.viewport_transform) / sizeof(u32);
190 set_block(viewport_trans_start, viewport_trans_size, DIRTY_REGS_POS(viewport_transform));
191
192 // Cullmode
193 constexpr u32 cull_mode_start = MAXWELL3D_REG_INDEX(cull);
194 constexpr u32 cull_mode_size = sizeof(regs.cull) / sizeof(u32);
195 set_block(cull_mode_start, cull_mode_size, DIRTY_REGS_POS(cull_mode));
196
197 // Screen y control
198 dirty_pointers[MAXWELL3D_REG_INDEX(screen_y_control)] = DIRTY_REGS_POS(screen_y_control);
199
200 // Primitive Restart
201 constexpr u32 primitive_restart_start = MAXWELL3D_REG_INDEX(primitive_restart);
202 constexpr u32 primitive_restart_size = sizeof(regs.primitive_restart) / sizeof(u32);
203 set_block(primitive_restart_start, primitive_restart_size, DIRTY_REGS_POS(primitive_restart));
204
205 // Depth Test
206 constexpr u8 depth_test_dirty_reg = DIRTY_REGS_POS(depth_test);
207 dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_enable)] = depth_test_dirty_reg;
208 dirty_pointers[MAXWELL3D_REG_INDEX(depth_write_enabled)] = depth_test_dirty_reg;
209 dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_func)] = depth_test_dirty_reg;
210
211 // Stencil Test
212 constexpr u32 stencil_test_dirty_reg = DIRTY_REGS_POS(stencil_test);
213 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_enable)] = stencil_test_dirty_reg;
214 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_func)] = stencil_test_dirty_reg;
215 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_ref)] = stencil_test_dirty_reg;
216 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_mask)] = stencil_test_dirty_reg;
217 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_fail)] = stencil_test_dirty_reg;
218 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zfail)] = stencil_test_dirty_reg;
219 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zpass)] = stencil_test_dirty_reg;
220 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_mask)] = stencil_test_dirty_reg;
221 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_two_side_enable)] = stencil_test_dirty_reg;
222 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_func)] = stencil_test_dirty_reg;
223 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_ref)] = stencil_test_dirty_reg;
224 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_mask)] = stencil_test_dirty_reg;
225 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_fail)] = stencil_test_dirty_reg;
226 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zfail)] = stencil_test_dirty_reg;
227 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zpass)] = stencil_test_dirty_reg;
228 dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_mask)] = stencil_test_dirty_reg;
229
230 // Color Mask
231 constexpr u8 color_mask_dirty_reg = DIRTY_REGS_POS(color_mask);
232 dirty_pointers[MAXWELL3D_REG_INDEX(color_mask_common)] = color_mask_dirty_reg;
233 set_block(MAXWELL3D_REG_INDEX(color_mask), sizeof(regs.color_mask) / sizeof(u32),
234 color_mask_dirty_reg);
235 // Blend State
236 constexpr u8 blend_state_dirty_reg = DIRTY_REGS_POS(blend_state);
237 set_block(MAXWELL3D_REG_INDEX(blend_color), sizeof(regs.blend_color) / sizeof(u32),
238 blend_state_dirty_reg);
239 dirty_pointers[MAXWELL3D_REG_INDEX(independent_blend_enable)] = blend_state_dirty_reg;
240 set_block(MAXWELL3D_REG_INDEX(blend), sizeof(regs.blend) / sizeof(u32), blend_state_dirty_reg);
241 set_block(MAXWELL3D_REG_INDEX(independent_blend), sizeof(regs.independent_blend) / sizeof(u32),
242 blend_state_dirty_reg);
243
244 // Scissor State
245 constexpr u8 scissor_test_dirty_reg = DIRTY_REGS_POS(scissor_test);
246 set_block(MAXWELL3D_REG_INDEX(scissor_test), sizeof(regs.scissor_test) / sizeof(u32),
247 scissor_test_dirty_reg);
248
249 // Polygon Offset
250 constexpr u8 polygon_offset_dirty_reg = DIRTY_REGS_POS(polygon_offset);
251 dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_fill_enable)] = polygon_offset_dirty_reg;
252 dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_line_enable)] = polygon_offset_dirty_reg;
253 dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_point_enable)] = polygon_offset_dirty_reg;
254 dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg;
255 dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg;
256 dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg;
257
258 // Depth bounds
259 constexpr u8 depth_bounds_values_dirty_reg = DIRTY_REGS_POS(depth_bounds_values);
260 dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[0])] = depth_bounds_values_dirty_reg;
261 dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[1])] = depth_bounds_values_dirty_reg;
262}
263
264void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) { 107void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) {
265 // Reset the current macro. 108 // Reset the current macro.
266 executing_macro = 0; 109 executing_macro = 0;
@@ -319,19 +162,9 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
319 162
320 if (regs.reg_array[method] != method_call.argument) { 163 if (regs.reg_array[method] != method_call.argument) {
321 regs.reg_array[method] = method_call.argument; 164 regs.reg_array[method] = method_call.argument;
322 const std::size_t dirty_reg = dirty_pointers[method]; 165
323 if (dirty_reg) { 166 for (const auto& table : dirty.tables) {
324 dirty.regs[dirty_reg] = true; 167 dirty.flags[table[method]] = true;
325 if (dirty_reg >= DIRTY_REGS_POS(vertex_array) &&
326 dirty_reg < DIRTY_REGS_POS(vertex_array_buffers)) {
327 dirty.vertex_array_buffers = true;
328 } else if (dirty_reg >= DIRTY_REGS_POS(vertex_instance) &&
329 dirty_reg < DIRTY_REGS_POS(vertex_instances)) {
330 dirty.vertex_instances = true;
331 } else if (dirty_reg >= DIRTY_REGS_POS(render_target) &&
332 dirty_reg < DIRTY_REGS_POS(render_settings)) {
333 dirty.render_settings = true;
334 }
335 } 168 }
336 } 169 }
337 170
@@ -419,7 +252,7 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
419 const bool is_last_call = method_call.IsLastCall(); 252 const bool is_last_call = method_call.IsLastCall();
420 upload_state.ProcessData(method_call.argument, is_last_call); 253 upload_state.ProcessData(method_call.argument, is_last_call);
421 if (is_last_call) { 254 if (is_last_call) {
422 dirty.OnMemoryWrite(); 255 OnMemoryWrite();
423 } 256 }
424 break; 257 break;
425 } 258 }
@@ -727,7 +560,7 @@ void Maxwell3D::FinishCBData() {
727 560
728 const u32 id = cb_data_state.id; 561 const u32 id = cb_data_state.id;
729 memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size); 562 memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
730 dirty.OnMemoryWrite(); 563 OnMemoryWrite();
731 564
732 cb_data_state.id = null_cb_data; 565 cb_data_state.id = null_cb_data;
733 cb_data_state.current = null_cb_data; 566 cb_data_state.current = null_cb_data;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 6ea7cc6a5..491cff370 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <limits>
9#include <optional> 10#include <optional>
10#include <type_traits> 11#include <type_traits>
11#include <unordered_map> 12#include <unordered_map>
@@ -431,21 +432,15 @@ public:
431 GeneratedPrimitives = 0x1F, 432 GeneratedPrimitives = 0x1F,
432 }; 433 };
433 434
434 struct Cull { 435 enum class FrontFace : u32 {
435 enum class FrontFace : u32 { 436 ClockWise = 0x0900,
436 ClockWise = 0x0900, 437 CounterClockWise = 0x0901,
437 CounterClockWise = 0x0901, 438 };
438 };
439
440 enum class CullFace : u32 {
441 Front = 0x0404,
442 Back = 0x0405,
443 FrontAndBack = 0x0408,
444 };
445 439
446 u32 enabled; 440 enum class CullFace : u32 {
447 FrontFace front_face; 441 Front = 0x0404,
448 CullFace cull_face; 442 Back = 0x0405,
443 FrontAndBack = 0x0408,
449 }; 444 };
450 445
451 struct Blend { 446 struct Blend {
@@ -574,7 +569,7 @@ public:
574 f32 translate_z; 569 f32 translate_z;
575 INSERT_UNION_PADDING_WORDS(2); 570 INSERT_UNION_PADDING_WORDS(2);
576 571
577 Common::Rectangle<s32> GetRect() const { 572 Common::Rectangle<f32> GetRect() const {
578 return { 573 return {
579 GetX(), // left 574 GetX(), // left
580 GetY() + GetHeight(), // top 575 GetY() + GetHeight(), // top
@@ -583,20 +578,20 @@ public:
583 }; 578 };
584 }; 579 };
585 580
586 s32 GetX() const { 581 f32 GetX() const {
587 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x))); 582 return std::max(0.0f, translate_x - std::fabs(scale_x));
588 } 583 }
589 584
590 s32 GetY() const { 585 f32 GetY() const {
591 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y))); 586 return std::max(0.0f, translate_y - std::fabs(scale_y));
592 } 587 }
593 588
594 s32 GetWidth() const { 589 f32 GetWidth() const {
595 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX(); 590 return translate_x + std::fabs(scale_x) - GetX();
596 } 591 }
597 592
598 s32 GetHeight() const { 593 f32 GetHeight() const {
599 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY(); 594 return translate_y + std::fabs(scale_y) - GetY();
600 } 595 }
601 }; 596 };
602 597
@@ -872,16 +867,7 @@ public:
872 867
873 INSERT_UNION_PADDING_WORDS(0x35); 868 INSERT_UNION_PADDING_WORDS(0x35);
874 869
875 union { 870 u32 clip_distance_enabled;
876 BitField<0, 1, u32> c0;
877 BitField<1, 1, u32> c1;
878 BitField<2, 1, u32> c2;
879 BitField<3, 1, u32> c3;
880 BitField<4, 1, u32> c4;
881 BitField<5, 1, u32> c5;
882 BitField<6, 1, u32> c6;
883 BitField<7, 1, u32> c7;
884 } clip_distance_enabled;
885 871
886 u32 samplecnt_enable; 872 u32 samplecnt_enable;
887 873
@@ -1060,7 +1046,9 @@ public:
1060 1046
1061 INSERT_UNION_PADDING_WORDS(1); 1047 INSERT_UNION_PADDING_WORDS(1);
1062 1048
1063 Cull cull; 1049 u32 cull_test_enabled;
1050 FrontFace front_face;
1051 CullFace cull_face;
1064 1052
1065 u32 pixel_center_integer; 1053 u32 pixel_center_integer;
1066 1054
@@ -1238,79 +1226,6 @@ public:
1238 1226
1239 State state{}; 1227 State state{};
1240 1228
1241 struct DirtyRegs {
1242 static constexpr std::size_t NUM_REGS = 256;
1243 static_assert(NUM_REGS - 1 <= std::numeric_limits<u8>::max());
1244
1245 union {
1246 struct {
1247 bool null_dirty;
1248
1249 // Vertex Attributes
1250 bool vertex_attrib_format;
1251
1252 // Vertex Arrays
1253 std::array<bool, 32> vertex_array;
1254
1255 bool vertex_array_buffers;
1256
1257 // Vertex Instances
1258 std::array<bool, 32> vertex_instance;
1259
1260 bool vertex_instances;
1261
1262 // Render Targets
1263 std::array<bool, 8> render_target;
1264 bool depth_buffer;
1265
1266 bool render_settings;
1267
1268 // Shaders
1269 bool shaders;
1270
1271 // Rasterizer State
1272 bool viewport;
1273 bool clip_coefficient;
1274 bool cull_mode;
1275 bool primitive_restart;
1276 bool depth_test;
1277 bool stencil_test;
1278 bool blend_state;
1279 bool scissor_test;
1280 bool transform_feedback;
1281 bool color_mask;
1282 bool polygon_offset;
1283 bool depth_bounds_values;
1284
1285 // Complementary
1286 bool viewport_transform;
1287 bool screen_y_control;
1288
1289 bool memory_general;
1290 };
1291 std::array<bool, NUM_REGS> regs;
1292 };
1293
1294 void ResetVertexArrays() {
1295 vertex_array.fill(true);
1296 vertex_array_buffers = true;
1297 }
1298
1299 void ResetRenderTargets() {
1300 depth_buffer = true;
1301 render_target.fill(true);
1302 render_settings = true;
1303 }
1304
1305 void OnMemoryWrite() {
1306 shaders = true;
1307 memory_general = true;
1308 ResetRenderTargets();
1309 ResetVertexArrays();
1310 }
1311
1312 } dirty{};
1313
1314 /// Reads a register value located at the input method address 1229 /// Reads a register value located at the input method address
1315 u32 GetRegisterValue(u32 method) const; 1230 u32 GetRegisterValue(u32 method) const;
1316 1231
@@ -1356,6 +1271,11 @@ public:
1356 return execute_on; 1271 return execute_on;
1357 } 1272 }
1358 1273
1274 /// Notify a memory write has happened.
1275 void OnMemoryWrite() {
1276 dirty.flags |= dirty.on_write_stores;
1277 }
1278
1359 enum class MMEDrawMode : u32 { 1279 enum class MMEDrawMode : u32 {
1360 Undefined, 1280 Undefined,
1361 Array, 1281 Array,
@@ -1371,6 +1291,16 @@ public:
1371 u32 gl_end_count{}; 1291 u32 gl_end_count{};
1372 } mme_draw; 1292 } mme_draw;
1373 1293
1294 struct DirtyState {
1295 using Flags = std::bitset<std::numeric_limits<u8>::max()>;
1296 using Table = std::array<u8, Regs::NUM_REGS>;
1297 using Tables = std::array<Table, 2>;
1298
1299 Flags flags;
1300 Flags on_write_stores;
1301 Tables tables{};
1302 } dirty;
1303
1374private: 1304private:
1375 void InitializeRegisterDefaults(); 1305 void InitializeRegisterDefaults();
1376 1306
@@ -1417,8 +1347,6 @@ private:
1417 /// Retrieves information about a specific TSC entry from the TSC buffer. 1347 /// Retrieves information about a specific TSC entry from the TSC buffer.
1418 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; 1348 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
1419 1349
1420 void InitDirtySettings();
1421
1422 /** 1350 /**
1423 * Call a macro on this engine. 1351 * Call a macro on this engine.
1424 * @param method Method to call 1352 * @param method Method to call
@@ -1561,7 +1489,9 @@ ASSERT_REG_POSITION(index_array, 0x5F2);
1561ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); 1489ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
1562ASSERT_REG_POSITION(instanced_arrays, 0x620); 1490ASSERT_REG_POSITION(instanced_arrays, 0x620);
1563ASSERT_REG_POSITION(vp_point_size, 0x644); 1491ASSERT_REG_POSITION(vp_point_size, 0x644);
1564ASSERT_REG_POSITION(cull, 0x646); 1492ASSERT_REG_POSITION(cull_test_enabled, 0x646);
1493ASSERT_REG_POSITION(front_face, 0x647);
1494ASSERT_REG_POSITION(cull_face, 0x648);
1565ASSERT_REG_POSITION(pixel_center_integer, 0x649); 1495ASSERT_REG_POSITION(pixel_center_integer, 0x649);
1566ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); 1496ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
1567ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); 1497ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index ad8453c5f..c2610f992 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -57,7 +57,7 @@ void MaxwellDMA::HandleCopy() {
57 } 57 }
58 58
59 // All copies here update the main memory, so mark all rasterizer states as invalid. 59 // All copies here update the main memory, so mark all rasterizer states as invalid.
60 system.GPU().Maxwell3D().dirty.OnMemoryWrite(); 60 system.GPU().Maxwell3D().OnMemoryWrite();
61 61
62 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) { 62 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
63 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D 63 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index f18eaf4bc..3e4514b94 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -89,6 +89,9 @@ public:
89 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, 89 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
90 const DiskResourceLoadCallback& callback = {}) {} 90 const DiskResourceLoadCallback& callback = {}) {}
91 91
92 /// Initializes renderer dirty flags
93 virtual void SetupDirtyFlags() {}
94
92 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. 95 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
93 GuestDriverProfile& AccessGuestDriverProfile() { 96 GuestDriverProfile& AccessGuestDriverProfile() {
94 return guest_driver_profile; 97 return guest_driver_profile;
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
index 874ed3c6e..b8a512cb6 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
@@ -11,7 +11,6 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/renderer_opengl/gl_framebuffer_cache.h" 13#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
14#include "video_core/renderer_opengl/gl_state.h"
15 14
16namespace OpenGL { 15namespace OpenGL {
17 16
@@ -36,8 +35,7 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
36 framebuffer.Create(); 35 framebuffer.Create();
37 36
38 // TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs. 37 // TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs.
39 local_state.draw.draw_framebuffer = framebuffer.handle; 38 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
40 local_state.ApplyFramebufferState();
41 39
42 if (key.zeta) { 40 if (key.zeta) {
43 const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil; 41 const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil;
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.h b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
index 02ec80ae9..8f698fee0 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
@@ -13,7 +13,6 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "video_core/engines/maxwell_3d.h" 14#include "video_core/engines/maxwell_3d.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_state.h"
17#include "video_core/renderer_opengl/gl_texture_cache.h" 16#include "video_core/renderer_opengl/gl_texture_cache.h"
18 17
19namespace OpenGL { 18namespace OpenGL {
@@ -63,7 +62,6 @@ public:
63private: 62private:
64 OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key); 63 OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key);
65 64
66 OpenGLState local_state;
67 std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache; 65 std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache;
68}; 66};
69 67
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3fcd319fd..4e4138573 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -55,6 +55,8 @@ MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255
55 55
56namespace { 56namespace {
57 57
58constexpr std::size_t NumSupportedVertexAttributes = 16;
59
58template <typename Engine, typename Entry> 60template <typename Engine, typename Entry>
59Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, 61Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
60 ShaderType shader_type, std::size_t index = 0) { 62 ShaderType shader_type, std::size_t index = 0) {
@@ -88,18 +90,23 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
88 return buffer.size; 90 return buffer.size;
89} 91}
90 92
93void oglEnable(GLenum cap, bool state) {
94 (state ? glEnable : glDisable)(cap);
95}
96
97void oglEnablei(GLenum cap, bool state, GLuint index) {
98 (state ? glEnablei : glDisablei)(cap, index);
99}
100
91} // Anonymous namespace 101} // Anonymous namespace
92 102
93RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, 103RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
94 ScreenInfo& info) 104 ScreenInfo& info, GLShader::ProgramManager& program_manager,
95 : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device}, 105 StateTracker& state_tracker)
106 : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker},
96 shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system}, 107 shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system},
97 screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { 108 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
98 shader_program_manager = std::make_unique<GLShader::ProgramManager>(); 109 buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
99 state.draw.shader_program = 0;
100 state.Apply();
101
102 LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here");
103 CheckExtensions(); 110 CheckExtensions();
104} 111}
105 112
@@ -113,93 +120,72 @@ void RasterizerOpenGL::CheckExtensions() {
113 } 120 }
114} 121}
115 122
116GLuint RasterizerOpenGL::SetupVertexFormat() { 123void RasterizerOpenGL::SetupVertexFormat() {
117 auto& gpu = system.GPU().Maxwell3D(); 124 auto& gpu = system.GPU().Maxwell3D();
118 const auto& regs = gpu.regs; 125 auto& flags = gpu.dirty.flags;
119 126 if (!flags[Dirty::VertexFormats]) {
120 if (!gpu.dirty.vertex_attrib_format) { 127 return;
121 return state.draw.vertex_array;
122 } 128 }
123 gpu.dirty.vertex_attrib_format = false; 129 flags[Dirty::VertexFormats] = false;
124 130
125 MICROPROFILE_SCOPE(OpenGL_VAO); 131 MICROPROFILE_SCOPE(OpenGL_VAO);
126 132
127 auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format); 133 // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. Enables
128 auto& vao_entry = iter->second; 134 // the first 16 vertex attributes always, as we don't know which ones are actually used until
129 135 // shader time. Note, Tegra technically supports 32, but we're capping this to 16 for now to
130 if (is_cache_miss) { 136 // avoid OpenGL errors.
131 vao_entry.Create(); 137 // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
132 const GLuint vao = vao_entry.handle; 138 // assume every shader uses them all.
133 139 for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) {
134 // Eventhough we are using DSA to create this vertex array, there is a bug on Intel's blob 140 if (!flags[Dirty::VertexFormat0 + index]) {
135 // that fails to properly create the vertex array if it's not bound even after creating it 141 continue;
136 // with glCreateVertexArrays
137 state.draw.vertex_array = vao;
138 state.ApplyVertexArrayState();
139
140 // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
141 // Enables the first 16 vertex attributes always, as we don't know which ones are actually
142 // used until shader time. Note, Tegra technically supports 32, but we're capping this to 16
143 // for now to avoid OpenGL errors.
144 // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
145 // assume every shader uses them all.
146 for (u32 index = 0; index < 16; ++index) {
147 const auto& attrib = regs.vertex_attrib_format[index];
148
149 // Ignore invalid attributes.
150 if (!attrib.IsValid())
151 continue;
152
153 const auto& buffer = regs.vertex_array[attrib.buffer];
154 LOG_TRACE(Render_OpenGL,
155 "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
156 index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
157 attrib.offset.Value(), attrib.IsNormalized());
158
159 ASSERT(buffer.IsEnabled());
160
161 glEnableVertexArrayAttrib(vao, index);
162 if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
163 attrib.type ==
164 Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
165 glVertexArrayAttribIFormat(vao, index, attrib.ComponentCount(),
166 MaxwellToGL::VertexType(attrib), attrib.offset);
167 } else {
168 glVertexArrayAttribFormat(
169 vao, index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
170 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
171 }
172 glVertexArrayAttribBinding(vao, index, attrib.buffer);
173 } 142 }
174 } 143 flags[Dirty::VertexFormat0 + index] = false;
144
145 const auto attrib = gpu.regs.vertex_attrib_format[index];
146 const auto gl_index = static_cast<GLuint>(index);
175 147
176 // Rebinding the VAO invalidates the vertex buffer bindings. 148 // Ignore invalid attributes.
177 gpu.dirty.ResetVertexArrays(); 149 if (!attrib.IsValid()) {
150 glDisableVertexAttribArray(gl_index);
151 continue;
152 }
153 glEnableVertexAttribArray(gl_index);
178 154
179 state.draw.vertex_array = vao_entry.handle; 155 if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
180 return vao_entry.handle; 156 attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
157 glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
158 MaxwellToGL::VertexType(attrib), attrib.offset);
159 } else {
160 glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
161 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
162 }
163 glVertexAttribBinding(gl_index, attrib.buffer);
164 }
181} 165}
182 166
183void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) { 167void RasterizerOpenGL::SetupVertexBuffer() {
184 auto& gpu = system.GPU().Maxwell3D(); 168 auto& gpu = system.GPU().Maxwell3D();
185 if (!gpu.dirty.vertex_array_buffers) 169 auto& flags = gpu.dirty.flags;
170 if (!flags[Dirty::VertexBuffers]) {
186 return; 171 return;
187 gpu.dirty.vertex_array_buffers = false; 172 }
188 173 flags[Dirty::VertexBuffers] = false;
189 const auto& regs = gpu.regs;
190 174
191 MICROPROFILE_SCOPE(OpenGL_VB); 175 MICROPROFILE_SCOPE(OpenGL_VB);
192 176
193 // Upload all guest vertex arrays sequentially to our buffer 177 // Upload all guest vertex arrays sequentially to our buffer
194 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 178 const auto& regs = gpu.regs;
195 if (!gpu.dirty.vertex_array[index]) 179 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
180 if (!flags[Dirty::VertexBuffer0 + index]) {
196 continue; 181 continue;
197 gpu.dirty.vertex_array[index] = false; 182 }
198 gpu.dirty.vertex_instance[index] = false; 183 flags[Dirty::VertexBuffer0 + index] = false;
199 184
200 const auto& vertex_array = regs.vertex_array[index]; 185 const auto& vertex_array = regs.vertex_array[index];
201 if (!vertex_array.IsEnabled()) 186 if (!vertex_array.IsEnabled()) {
202 continue; 187 continue;
188 }
203 189
204 const GPUVAddr start = vertex_array.StartAddress(); 190 const GPUVAddr start = vertex_array.StartAddress();
205 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 191 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
@@ -209,42 +195,30 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
209 const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); 195 const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size);
210 196
211 // Bind the vertex array to the buffer at the current offset. 197 // Bind the vertex array to the buffer at the current offset.
212 vertex_array_pushbuffer.SetVertexBuffer(index, vertex_buffer, vertex_buffer_offset, 198 vertex_array_pushbuffer.SetVertexBuffer(static_cast<GLuint>(index), vertex_buffer,
213 vertex_array.stride); 199 vertex_buffer_offset, vertex_array.stride);
214
215 if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
216 // Enable vertex buffer instancing with the specified divisor.
217 glVertexArrayBindingDivisor(vao, index, vertex_array.divisor);
218 } else {
219 // Disable the vertex buffer instancing.
220 glVertexArrayBindingDivisor(vao, index, 0);
221 }
222 } 200 }
223} 201}
224 202
225void RasterizerOpenGL::SetupVertexInstances(GLuint vao) { 203void RasterizerOpenGL::SetupVertexInstances() {
226 auto& gpu = system.GPU().Maxwell3D(); 204 auto& gpu = system.GPU().Maxwell3D();
227 205 auto& flags = gpu.dirty.flags;
228 if (!gpu.dirty.vertex_instances) 206 if (!flags[Dirty::VertexInstances]) {
229 return; 207 return;
230 gpu.dirty.vertex_instances = false; 208 }
209 flags[Dirty::VertexInstances] = false;
231 210
232 const auto& regs = gpu.regs; 211 const auto& regs = gpu.regs;
233 // Upload all guest vertex arrays sequentially to our buffer 212 for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) {
234 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 213 if (!flags[Dirty::VertexInstance0 + index]) {
235 if (!gpu.dirty.vertex_instance[index])
236 continue; 214 continue;
237
238 gpu.dirty.vertex_instance[index] = false;
239
240 if (regs.instanced_arrays.IsInstancingEnabled(index) &&
241 regs.vertex_array[index].divisor != 0) {
242 // Enable vertex buffer instancing with the specified divisor.
243 glVertexArrayBindingDivisor(vao, index, regs.vertex_array[index].divisor);
244 } else {
245 // Disable the vertex buffer instancing.
246 glVertexArrayBindingDivisor(vao, index, 0);
247 } 215 }
216 flags[Dirty::VertexInstance0 + index] = false;
217
218 const auto gl_index = static_cast<GLuint>(index);
219 const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index);
220 const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0;
221 glVertexBindingDivisor(gl_index, divisor);
248 } 222 }
249} 223}
250 224
@@ -260,8 +234,7 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
260void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { 234void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
261 MICROPROFILE_SCOPE(OpenGL_Shader); 235 MICROPROFILE_SCOPE(OpenGL_Shader);
262 auto& gpu = system.GPU().Maxwell3D(); 236 auto& gpu = system.GPU().Maxwell3D();
263 237 u32 clip_distances = 0;
264 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
265 238
266 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 239 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
267 const auto& shader_config = gpu.regs.shader_config[index]; 240 const auto& shader_config = gpu.regs.shader_config[index];
@@ -271,10 +244,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
271 if (!gpu.regs.IsShaderConfigEnabled(index)) { 244 if (!gpu.regs.IsShaderConfigEnabled(index)) {
272 switch (program) { 245 switch (program) {
273 case Maxwell::ShaderProgram::Geometry: 246 case Maxwell::ShaderProgram::Geometry:
274 shader_program_manager->UseTrivialGeometryShader(); 247 program_manager.UseGeometryShader(0);
275 break; 248 break;
276 case Maxwell::ShaderProgram::Fragment: 249 case Maxwell::ShaderProgram::Fragment:
277 shader_program_manager->UseTrivialFragmentShader(); 250 program_manager.UseFragmentShader(0);
278 break; 251 break;
279 default: 252 default:
280 break; 253 break;
@@ -305,13 +278,13 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
305 switch (program) { 278 switch (program) {
306 case Maxwell::ShaderProgram::VertexA: 279 case Maxwell::ShaderProgram::VertexA:
307 case Maxwell::ShaderProgram::VertexB: 280 case Maxwell::ShaderProgram::VertexB:
308 shader_program_manager->UseProgrammableVertexShader(program_handle); 281 program_manager.UseVertexShader(program_handle);
309 break; 282 break;
310 case Maxwell::ShaderProgram::Geometry: 283 case Maxwell::ShaderProgram::Geometry:
311 shader_program_manager->UseProgrammableGeometryShader(program_handle); 284 program_manager.UseGeometryShader(program_handle);
312 break; 285 break;
313 case Maxwell::ShaderProgram::Fragment: 286 case Maxwell::ShaderProgram::Fragment:
314 shader_program_manager->UseProgrammableFragmentShader(program_handle); 287 program_manager.UseFragmentShader(program_handle);
315 break; 288 break;
316 default: 289 default:
317 UNIMPLEMENTED_MSG("Unimplemented shader index={}, enable={}, offset=0x{:08X}", index, 290 UNIMPLEMENTED_MSG("Unimplemented shader index={}, enable={}, offset=0x{:08X}", index,
@@ -322,9 +295,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
322 // When a clip distance is enabled but not set in the shader it crops parts of the screen 295 // When a clip distance is enabled but not set in the shader it crops parts of the screen
323 // (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the 296 // (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
324 // clip distances only when it's written by a shader stage. 297 // clip distances only when it's written by a shader stage.
325 for (std::size_t i = 0; i < Maxwell::NumClipDistances; ++i) { 298 clip_distances |= shader->GetShaderEntries().clip_distances;
326 clip_distances[i] = clip_distances[i] || shader->GetShaderEntries().clip_distances[i];
327 }
328 299
329 // When VertexA is enabled, we have dual vertex shaders 300 // When VertexA is enabled, we have dual vertex shaders
330 if (program == Maxwell::ShaderProgram::VertexA) { 301 if (program == Maxwell::ShaderProgram::VertexA) {
@@ -334,8 +305,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
334 } 305 }
335 306
336 SyncClipEnabled(clip_distances); 307 SyncClipEnabled(clip_distances);
337 308 gpu.dirty.flags[Dirty::Shaders] = false;
338 gpu.dirty.shaders = false;
339} 309}
340 310
341std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 311std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -368,20 +338,23 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
368 shader_cache.LoadDiskCache(stop_loading, callback); 338 shader_cache.LoadDiskCache(stop_loading, callback);
369} 339}
370 340
341void RasterizerOpenGL::SetupDirtyFlags() {
342 state_tracker.Initialize();
343}
344
371void RasterizerOpenGL::ConfigureFramebuffers() { 345void RasterizerOpenGL::ConfigureFramebuffers() {
372 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 346 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
373 auto& gpu = system.GPU().Maxwell3D(); 347 auto& gpu = system.GPU().Maxwell3D();
374 if (!gpu.dirty.render_settings) { 348 if (!gpu.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
375 return; 349 return;
376 } 350 }
377 gpu.dirty.render_settings = false; 351 gpu.dirty.flags[VideoCommon::Dirty::RenderTargets] = false;
378 352
379 texture_cache.GuardRenderTargets(true); 353 texture_cache.GuardRenderTargets(true);
380 354
381 View depth_surface = texture_cache.GetDepthBufferSurface(true); 355 View depth_surface = texture_cache.GetDepthBufferSurface(true);
382 356
383 const auto& regs = gpu.regs; 357 const auto& regs = gpu.regs;
384 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
385 UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); 358 UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
386 359
387 // Bind the framebuffer surfaces 360 // Bind the framebuffer surfaces
@@ -409,14 +382,11 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
409 382
410 texture_cache.GuardRenderTargets(false); 383 texture_cache.GuardRenderTargets(false);
411 384
412 state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key); 385 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
413 SyncViewport(state);
414} 386}
415 387
416void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, 388void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb,
417 bool using_depth_fb, bool using_stencil_fb) { 389 bool using_stencil_fb) {
418 using VideoCore::Surface::SurfaceType;
419
420 auto& gpu = system.GPU().Maxwell3D(); 390 auto& gpu = system.GPU().Maxwell3D();
421 const auto& regs = gpu.regs; 391 const auto& regs = gpu.regs;
422 392
@@ -435,80 +405,44 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, boo
435 key.colors[0] = color_surface; 405 key.colors[0] = color_surface;
436 key.zeta = depth_surface; 406 key.zeta = depth_surface;
437 407
438 current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key); 408 state_tracker.NotifyFramebuffer();
439 current_state.ApplyFramebufferState(); 409 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
440} 410}
441 411
442void RasterizerOpenGL::Clear() { 412void RasterizerOpenGL::Clear() {
443 const auto& maxwell3d = system.GPU().Maxwell3D(); 413 const auto& gpu = system.GPU().Maxwell3D();
444 414 if (!gpu.ShouldExecute()) {
445 if (!maxwell3d.ShouldExecute()) {
446 return; 415 return;
447 } 416 }
448 417
449 const auto& regs = maxwell3d.regs; 418 const auto& regs = gpu.regs;
450 bool use_color{}; 419 bool use_color{};
451 bool use_depth{}; 420 bool use_depth{};
452 bool use_stencil{}; 421 bool use_stencil{};
453 422
454 OpenGLState prev_state{OpenGLState::GetCurState()};
455 SCOPE_EXIT({
456 prev_state.AllDirty();
457 prev_state.Apply();
458 });
459
460 OpenGLState clear_state{OpenGLState::GetCurState()};
461 clear_state.SetDefaultViewports();
462 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 423 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
463 regs.clear_buffers.A) { 424 regs.clear_buffers.A) {
464 use_color = true; 425 use_color = true;
465 } 426 }
466 if (use_color) { 427 if (use_color) {
467 clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; 428 state_tracker.NotifyColorMask0();
468 clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; 429 glColorMaski(0, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0,
469 clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; 430 regs.clear_buffers.B != 0, regs.clear_buffers.A != 0);
470 clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; 431
432 // TODO(Rodrigo): Determine if clamping is used on clears
433 SyncFragmentColorClampState();
434 SyncFramebufferSRGB();
471 } 435 }
472 if (regs.clear_buffers.Z) { 436 if (regs.clear_buffers.Z) {
473 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 437 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
474 use_depth = true; 438 use_depth = true;
475 439
476 // Always enable the depth write when clearing the depth buffer. The depth write mask is 440 state_tracker.NotifyDepthMask();
477 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to 441 glDepthMask(GL_TRUE);
478 // true.
479 clear_state.depth.test_enabled = true;
480 clear_state.depth.test_func = GL_ALWAYS;
481 clear_state.depth.write_mask = GL_TRUE;
482 } 442 }
483 if (regs.clear_buffers.S) { 443 if (regs.clear_buffers.S) {
484 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); 444 ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
485 use_stencil = true; 445 use_stencil = true;
486 clear_state.stencil.test_enabled = true;
487
488 if (regs.clear_flags.stencil) {
489 // Stencil affects the clear so fill it with the used masks
490 clear_state.stencil.front.test_func = GL_ALWAYS;
491 clear_state.stencil.front.test_mask = regs.stencil_front_func_mask;
492 clear_state.stencil.front.action_stencil_fail = GL_KEEP;
493 clear_state.stencil.front.action_depth_fail = GL_KEEP;
494 clear_state.stencil.front.action_depth_pass = GL_KEEP;
495 clear_state.stencil.front.write_mask = regs.stencil_front_mask;
496 if (regs.stencil_two_side_enable) {
497 clear_state.stencil.back.test_func = GL_ALWAYS;
498 clear_state.stencil.back.test_mask = regs.stencil_back_func_mask;
499 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
500 clear_state.stencil.back.action_depth_fail = GL_KEEP;
501 clear_state.stencil.back.action_depth_pass = GL_KEEP;
502 clear_state.stencil.back.write_mask = regs.stencil_back_mask;
503 } else {
504 clear_state.stencil.back.test_func = GL_ALWAYS;
505 clear_state.stencil.back.test_mask = 0xFFFFFFFF;
506 clear_state.stencil.back.write_mask = 0xFFFFFFFF;
507 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
508 clear_state.stencil.back.action_depth_fail = GL_KEEP;
509 clear_state.stencil.back.action_depth_pass = GL_KEEP;
510 }
511 }
512 } 446 }
513 447
514 if (!use_color && !use_depth && !use_stencil) { 448 if (!use_color && !use_depth && !use_stencil) {
@@ -516,20 +450,18 @@ void RasterizerOpenGL::Clear() {
516 return; 450 return;
517 } 451 }
518 452
519 ConfigureClearFramebuffer(clear_state, use_color, use_depth, use_stencil); 453 SyncRasterizeEnable();
520 454
521 SyncViewport(clear_state);
522 SyncRasterizeEnable(clear_state);
523 if (regs.clear_flags.scissor) { 455 if (regs.clear_flags.scissor) {
524 SyncScissorTest(clear_state); 456 SyncScissorTest();
457 } else {
458 state_tracker.NotifyScissor0();
459 glDisablei(GL_SCISSOR_TEST, 0);
525 } 460 }
526 461
527 if (regs.clear_flags.viewport) { 462 UNIMPLEMENTED_IF(regs.clear_flags.viewport);
528 clear_state.EmulateViewportWithScissor();
529 }
530 463
531 clear_state.AllDirty(); 464 ConfigureClearFramebuffer(use_color, use_depth, use_stencil);
532 clear_state.Apply();
533 465
534 if (use_color) { 466 if (use_color) {
535 glClearBufferfv(GL_COLOR, 0, regs.clear_color); 467 glClearBufferfv(GL_COLOR, 0, regs.clear_color);
@@ -553,21 +485,24 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
553 485
554 query_cache.UpdateCounters(); 486 query_cache.UpdateCounters();
555 487
556 SyncRasterizeEnable(state); 488 SyncViewport();
489 SyncRasterizeEnable();
557 SyncColorMask(); 490 SyncColorMask();
558 SyncFragmentColorClampState(); 491 SyncFragmentColorClampState();
559 SyncMultiSampleState(); 492 SyncMultiSampleState();
560 SyncDepthTestState(); 493 SyncDepthTestState();
494 SyncDepthClamp();
561 SyncStencilTestState(); 495 SyncStencilTestState();
562 SyncBlendState(); 496 SyncBlendState();
563 SyncLogicOpState(); 497 SyncLogicOpState();
564 SyncCullMode(); 498 SyncCullMode();
565 SyncPrimitiveRestart(); 499 SyncPrimitiveRestart();
566 SyncScissorTest(state); 500 SyncScissorTest();
567 SyncTransformFeedback(); 501 SyncTransformFeedback();
568 SyncPointState(); 502 SyncPointState();
569 SyncPolygonOffset(); 503 SyncPolygonOffset();
570 SyncAlphaTest(); 504 SyncAlphaTest();
505 SyncFramebufferSRGB();
571 506
572 buffer_cache.Acquire(); 507 buffer_cache.Acquire();
573 508
@@ -591,13 +526,12 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
591 buffer_cache.Map(buffer_size); 526 buffer_cache.Map(buffer_size);
592 527
593 // Prepare vertex array format. 528 // Prepare vertex array format.
594 const GLuint vao = SetupVertexFormat(); 529 SetupVertexFormat();
595 vertex_array_pushbuffer.Setup(vao); 530 vertex_array_pushbuffer.Setup();
596 531
597 // Upload vertex and index data. 532 // Upload vertex and index data.
598 SetupVertexBuffer(vao); 533 SetupVertexBuffer();
599 SetupVertexInstances(vao); 534 SetupVertexInstances();
600
601 GLintptr index_buffer_offset; 535 GLintptr index_buffer_offset;
602 if (is_indexed) { 536 if (is_indexed) {
603 index_buffer_offset = SetupIndexBuffer(); 537 index_buffer_offset = SetupIndexBuffer();
@@ -631,14 +565,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
631 bind_ubo_pushbuffer.Bind(); 565 bind_ubo_pushbuffer.Bind();
632 bind_ssbo_pushbuffer.Bind(); 566 bind_ssbo_pushbuffer.Bind();
633 567
634 if (invalidate) { 568 program_manager.BindGraphicsPipeline();
635 // As all cached buffers are invalidated, we need to recheck their state.
636 gpu.dirty.ResetVertexArrays();
637 }
638 gpu.dirty.memory_general = false;
639
640 shader_program_manager->ApplyTo(state);
641 state.Apply();
642 569
643 if (texture_cache.TextureBarrier()) { 570 if (texture_cache.TextureBarrier()) {
644 glTextureBarrier(); 571 glTextureBarrier();
@@ -700,8 +627,7 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
700 const ProgramVariant variant(launch_desc.block_dim_x, launch_desc.block_dim_y, 627 const ProgramVariant variant(launch_desc.block_dim_x, launch_desc.block_dim_y,
701 launch_desc.block_dim_z, launch_desc.shared_alloc, 628 launch_desc.block_dim_z, launch_desc.shared_alloc,
702 launch_desc.local_pos_alloc); 629 launch_desc.local_pos_alloc);
703 state.draw.shader_program = kernel->GetHandle(variant); 630 program_manager.BindComputeShader(kernel->GetHandle(variant));
704 state.draw.program_pipeline = 0;
705 631
706 const std::size_t buffer_size = 632 const std::size_t buffer_size =
707 Tegra::Engines::KeplerCompute::NumConstBuffers * 633 Tegra::Engines::KeplerCompute::NumConstBuffers *
@@ -719,11 +645,6 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
719 bind_ubo_pushbuffer.Bind(); 645 bind_ubo_pushbuffer.Bind();
720 bind_ssbo_pushbuffer.Bind(); 646 bind_ssbo_pushbuffer.Bind();
721 647
722 state.ApplyTextures();
723 state.ApplyImages();
724 state.ApplyShaderProgram();
725 state.ApplyProgramPipeline();
726
727 glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z); 648 glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
728 ++num_queued_commands; 649 ++num_queued_commands;
729} 650}
@@ -935,20 +856,20 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
935 const auto view = texture_cache.GetTextureSurface(texture.tic, entry); 856 const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
936 if (!view) { 857 if (!view) {
937 // Can occur when texture addr is null or its memory is unmapped/invalid 858 // Can occur when texture addr is null or its memory is unmapped/invalid
938 state.samplers[binding] = 0; 859 glBindSampler(binding, 0);
939 state.textures[binding] = 0; 860 glBindTextureUnit(binding, 0);
940 return; 861 return;
941 } 862 }
942 state.textures[binding] = view->GetTexture(); 863 glBindTextureUnit(binding, view->GetTexture());
943 864
944 if (view->GetSurfaceParams().IsBuffer()) { 865 if (view->GetSurfaceParams().IsBuffer()) {
945 return; 866 return;
946 } 867 }
947 state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
948
949 // Apply swizzle to textures that are not buffers. 868 // Apply swizzle to textures that are not buffers.
950 view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source, 869 view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
951 texture.tic.w_source); 870 texture.tic.w_source);
871
872 glBindSampler(binding, sampler_cache.GetSampler(texture.tsc));
952} 873}
953 874
954void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) { 875void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) {
@@ -974,7 +895,7 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
974 const GLShader::ImageEntry& entry) { 895 const GLShader::ImageEntry& entry) {
975 const auto view = texture_cache.GetImageSurface(tic, entry); 896 const auto view = texture_cache.GetImageSurface(tic, entry);
976 if (!view) { 897 if (!view) {
977 state.images[binding] = 0; 898 glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
978 return; 899 return;
979 } 900 }
980 if (!tic.IsBuffer()) { 901 if (!tic.IsBuffer()) {
@@ -983,55 +904,85 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
983 if (entry.IsWritten()) { 904 if (entry.IsWritten()) {
984 view->MarkAsModified(texture_cache.Tick()); 905 view->MarkAsModified(texture_cache.Tick());
985 } 906 }
986 state.images[binding] = view->GetTexture(); 907 glBindImageTexture(binding, view->GetTexture(), 0, GL_TRUE, 0, GL_READ_WRITE,
908 view->GetFormat());
987} 909}
988 910
989void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 911void RasterizerOpenGL::SyncViewport() {
990 const auto& regs = system.GPU().Maxwell3D().regs; 912 auto& gpu = system.GPU().Maxwell3D();
991 const bool geometry_shaders_enabled = 913 auto& flags = gpu.dirty.flags;
992 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); 914 const auto& regs = gpu.regs;
993 const std::size_t viewport_count = 915
994 geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1; 916 const bool dirty_viewport = flags[Dirty::Viewports];
995 for (std::size_t i = 0; i < viewport_count; i++) { 917 if (dirty_viewport || flags[Dirty::ClipControl]) {
996 auto& viewport = current_state.viewports[i]; 918 flags[Dirty::ClipControl] = false;
997 const auto& src = regs.viewports[i]; 919
998 const Common::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; 920 bool flip_y = false;
999 viewport.x = viewport_rect.left; 921 if (regs.viewport_transform[0].scale_y < 0.0) {
1000 viewport.y = viewport_rect.bottom; 922 flip_y = !flip_y;
1001 viewport.width = viewport_rect.GetWidth(); 923 }
1002 viewport.height = viewport_rect.GetHeight(); 924 if (regs.screen_y_control.y_negate != 0) {
1003 viewport.depth_range_far = src.depth_range_far; 925 flip_y = !flip_y;
1004 viewport.depth_range_near = src.depth_range_near; 926 }
1005 } 927 glClipControl(flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT,
1006 state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0; 928 regs.depth_mode == Maxwell::DepthMode::ZeroToOne ? GL_ZERO_TO_ONE
1007 state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0; 929 : GL_NEGATIVE_ONE_TO_ONE);
1008 930 }
1009 bool flip_y = false; 931
1010 if (regs.viewport_transform[0].scale_y < 0.0) { 932 if (dirty_viewport) {
1011 flip_y = !flip_y; 933 flags[Dirty::Viewports] = false;
1012 } 934
1013 if (regs.screen_y_control.y_negate != 0) { 935 const bool force = flags[Dirty::ViewportTransform];
1014 flip_y = !flip_y; 936 flags[Dirty::ViewportTransform] = false;
1015 } 937
1016 state.clip_control.origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT; 938 for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
1017 state.clip_control.depth_mode = 939 if (!force && !flags[Dirty::Viewport0 + i]) {
1018 regs.depth_mode == Tegra::Engines::Maxwell3D::Regs::DepthMode::ZeroToOne 940 continue;
1019 ? GL_ZERO_TO_ONE 941 }
1020 : GL_NEGATIVE_ONE_TO_ONE; 942 flags[Dirty::Viewport0 + i] = false;
943
944 const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
945 glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
946 rect.GetHeight());
947
948 const auto& src = regs.viewports[i];
949 glDepthRangeIndexed(static_cast<GLuint>(i), static_cast<GLdouble>(src.depth_range_near),
950 static_cast<GLdouble>(src.depth_range_far));
951 }
952 }
1021} 953}
1022 954
1023void RasterizerOpenGL::SyncClipEnabled( 955void RasterizerOpenGL::SyncDepthClamp() {
1024 const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) { 956 auto& gpu = system.GPU().Maxwell3D();
957 auto& flags = gpu.dirty.flags;
958 if (!flags[Dirty::DepthClampEnabled]) {
959 return;
960 }
961 flags[Dirty::DepthClampEnabled] = false;
1025 962
1026 const auto& regs = system.GPU().Maxwell3D().regs; 963 const auto& state = gpu.regs.view_volume_clip_control;
1027 const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{ 964 UNIMPLEMENTED_IF_MSG(state.depth_clamp_far != state.depth_clamp_near,
1028 regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0, 965 "Unimplemented depth clamp separation!");
1029 regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0, 966
1030 regs.clip_distance_enabled.c4 != 0, regs.clip_distance_enabled.c5 != 0, 967 oglEnable(GL_DEPTH_CLAMP, state.depth_clamp_far || state.depth_clamp_near);
1031 regs.clip_distance_enabled.c6 != 0, regs.clip_distance_enabled.c7 != 0}; 968}
969
970void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
971 auto& gpu = system.GPU().Maxwell3D();
972 auto& flags = gpu.dirty.flags;
973 if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) {
974 return;
975 }
976 flags[Dirty::ClipDistances] = false;
977
978 clip_mask &= gpu.regs.clip_distance_enabled;
979 if (clip_mask == last_clip_distance_mask) {
980 return;
981 }
982 last_clip_distance_mask = clip_mask;
1032 983
1033 for (std::size_t i = 0; i < Maxwell::Regs::NumClipDistances; ++i) { 984 for (std::size_t i = 0; i < Maxwell::Regs::NumClipDistances; ++i) {
1034 state.clip_distance[i] = reg_state[i] && clip_mask[i]; 985 oglEnable(static_cast<GLenum>(GL_CLIP_DISTANCE0 + i), (clip_mask >> i) & 1);
1035 } 986 }
1036} 987}
1037 988
@@ -1040,199 +991,269 @@ void RasterizerOpenGL::SyncClipCoef() {
1040} 991}
1041 992
1042void RasterizerOpenGL::SyncCullMode() { 993void RasterizerOpenGL::SyncCullMode() {
1043 const auto& regs = system.GPU().Maxwell3D().regs; 994 auto& gpu = system.GPU().Maxwell3D();
995 auto& flags = gpu.dirty.flags;
996 const auto& regs = gpu.regs;
1044 997
1045 state.cull.enabled = regs.cull.enabled != 0; 998 if (flags[Dirty::CullTest]) {
1046 if (state.cull.enabled) { 999 flags[Dirty::CullTest] = false;
1047 state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face); 1000
1001 if (regs.cull_test_enabled) {
1002 glEnable(GL_CULL_FACE);
1003 glCullFace(MaxwellToGL::CullFace(regs.cull_face));
1004 } else {
1005 glDisable(GL_CULL_FACE);
1006 }
1048 } 1007 }
1049 1008
1050 state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); 1009 if (flags[Dirty::FrontFace]) {
1010 flags[Dirty::FrontFace] = false;
1011 glFrontFace(MaxwellToGL::FrontFace(regs.front_face));
1012 }
1051} 1013}
1052 1014
1053void RasterizerOpenGL::SyncPrimitiveRestart() { 1015void RasterizerOpenGL::SyncPrimitiveRestart() {
1054 const auto& regs = system.GPU().Maxwell3D().regs; 1016 auto& gpu = system.GPU().Maxwell3D();
1017 auto& flags = gpu.dirty.flags;
1018 if (!flags[Dirty::PrimitiveRestart]) {
1019 return;
1020 }
1021 flags[Dirty::PrimitiveRestart] = false;
1055 1022
1056 state.primitive_restart.enabled = regs.primitive_restart.enabled; 1023 if (gpu.regs.primitive_restart.enabled) {
1057 state.primitive_restart.index = regs.primitive_restart.index; 1024 glEnable(GL_PRIMITIVE_RESTART);
1025 glPrimitiveRestartIndex(gpu.regs.primitive_restart.index);
1026 } else {
1027 glDisable(GL_PRIMITIVE_RESTART);
1028 }
1058} 1029}
1059 1030
1060void RasterizerOpenGL::SyncDepthTestState() { 1031void RasterizerOpenGL::SyncDepthTestState() {
1061 const auto& regs = system.GPU().Maxwell3D().regs; 1032 auto& gpu = system.GPU().Maxwell3D();
1062 1033 auto& flags = gpu.dirty.flags;
1063 state.depth.test_enabled = regs.depth_test_enable != 0;
1064 state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
1065 1034
1066 if (!state.depth.test_enabled) { 1035 const auto& regs = gpu.regs;
1067 return; 1036 if (flags[Dirty::DepthMask]) {
1037 flags[Dirty::DepthMask] = false;
1038 glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
1068 } 1039 }
1069 1040
1070 state.depth.test_func = MaxwellToGL::ComparisonOp(regs.depth_test_func); 1041 if (flags[Dirty::DepthTest]) {
1042 flags[Dirty::DepthTest] = false;
1043 if (regs.depth_test_enable) {
1044 glEnable(GL_DEPTH_TEST);
1045 glDepthFunc(MaxwellToGL::ComparisonOp(regs.depth_test_func));
1046 } else {
1047 glDisable(GL_DEPTH_TEST);
1048 }
1049 }
1071} 1050}
1072 1051
1073void RasterizerOpenGL::SyncStencilTestState() { 1052void RasterizerOpenGL::SyncStencilTestState() {
1074 auto& maxwell3d = system.GPU().Maxwell3D(); 1053 auto& gpu = system.GPU().Maxwell3D();
1075 if (!maxwell3d.dirty.stencil_test) { 1054 auto& flags = gpu.dirty.flags;
1055 if (!flags[Dirty::StencilTest]) {
1076 return; 1056 return;
1077 } 1057 }
1078 maxwell3d.dirty.stencil_test = false; 1058 flags[Dirty::StencilTest] = false;
1079
1080 const auto& regs = maxwell3d.regs;
1081 state.stencil.test_enabled = regs.stencil_enable != 0;
1082 state.MarkDirtyStencilState();
1083 1059
1060 const auto& regs = gpu.regs;
1084 if (!regs.stencil_enable) { 1061 if (!regs.stencil_enable) {
1062 glDisable(GL_STENCIL_TEST);
1085 return; 1063 return;
1086 } 1064 }
1087 1065
1088 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func); 1066 glEnable(GL_STENCIL_TEST);
1089 state.stencil.front.test_ref = regs.stencil_front_func_ref; 1067 glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
1090 state.stencil.front.test_mask = regs.stencil_front_func_mask; 1068 regs.stencil_front_func_ref, regs.stencil_front_func_mask);
1091 state.stencil.front.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_fail); 1069 glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail),
1092 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail); 1070 MaxwellToGL::StencilOp(regs.stencil_front_op_zfail),
1093 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass); 1071 MaxwellToGL::StencilOp(regs.stencil_front_op_zpass));
1094 state.stencil.front.write_mask = regs.stencil_front_mask; 1072 glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask);
1073
1095 if (regs.stencil_two_side_enable) { 1074 if (regs.stencil_two_side_enable) {
1096 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); 1075 glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func),
1097 state.stencil.back.test_ref = regs.stencil_back_func_ref; 1076 regs.stencil_back_func_ref, regs.stencil_back_func_mask);
1098 state.stencil.back.test_mask = regs.stencil_back_func_mask; 1077 glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail),
1099 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); 1078 MaxwellToGL::StencilOp(regs.stencil_back_op_zfail),
1100 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); 1079 MaxwellToGL::StencilOp(regs.stencil_back_op_zpass));
1101 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); 1080 glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask);
1102 state.stencil.back.write_mask = regs.stencil_back_mask;
1103 } else { 1081 } else {
1104 state.stencil.back.test_func = GL_ALWAYS; 1082 glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF);
1105 state.stencil.back.test_ref = 0; 1083 glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
1106 state.stencil.back.test_mask = 0xFFFFFFFF; 1084 glStencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
1107 state.stencil.back.write_mask = 0xFFFFFFFF;
1108 state.stencil.back.action_stencil_fail = GL_KEEP;
1109 state.stencil.back.action_depth_fail = GL_KEEP;
1110 state.stencil.back.action_depth_pass = GL_KEEP;
1111 } 1085 }
1112} 1086}
1113 1087
1114void RasterizerOpenGL::SyncRasterizeEnable(OpenGLState& current_state) { 1088void RasterizerOpenGL::SyncRasterizeEnable() {
1115 const auto& regs = system.GPU().Maxwell3D().regs; 1089 auto& gpu = system.GPU().Maxwell3D();
1116 current_state.rasterizer_discard = regs.rasterize_enable == 0; 1090 auto& flags = gpu.dirty.flags;
1091 if (!flags[Dirty::RasterizeEnable]) {
1092 return;
1093 }
1094 flags[Dirty::RasterizeEnable] = false;
1095
1096 oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0);
1117} 1097}
1118 1098
1119void RasterizerOpenGL::SyncColorMask() { 1099void RasterizerOpenGL::SyncColorMask() {
1120 auto& maxwell3d = system.GPU().Maxwell3D(); 1100 auto& gpu = system.GPU().Maxwell3D();
1121 if (!maxwell3d.dirty.color_mask) { 1101 auto& flags = gpu.dirty.flags;
1102 if (!flags[Dirty::ColorMasks]) {
1122 return; 1103 return;
1123 } 1104 }
1124 const auto& regs = maxwell3d.regs; 1105 flags[Dirty::ColorMasks] = false;
1125 1106
1126 const std::size_t count = 1107 const bool force = flags[Dirty::ColorMaskCommon];
1127 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1; 1108 flags[Dirty::ColorMaskCommon] = false;
1128 for (std::size_t i = 0; i < count; i++) { 1109
1129 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i]; 1110 const auto& regs = gpu.regs;
1130 auto& dest = state.color_mask[i]; 1111 if (regs.color_mask_common) {
1131 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE; 1112 if (!force && !flags[Dirty::ColorMask0]) {
1132 dest.green_enabled = (source.G == 0) ? GL_FALSE : GL_TRUE; 1113 return;
1133 dest.blue_enabled = (source.B == 0) ? GL_FALSE : GL_TRUE; 1114 }
1134 dest.alpha_enabled = (source.A == 0) ? GL_FALSE : GL_TRUE; 1115 flags[Dirty::ColorMask0] = false;
1116
1117 auto& mask = regs.color_mask[0];
1118 glColorMask(mask.R != 0, mask.B != 0, mask.G != 0, mask.A != 0);
1119 return;
1135 } 1120 }
1136 1121
1137 state.MarkDirtyColorMask(); 1122 // Path without color_mask_common set
1138 maxwell3d.dirty.color_mask = false; 1123 for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
1124 if (!force && !flags[Dirty::ColorMask0 + i]) {
1125 continue;
1126 }
1127 flags[Dirty::ColorMask0 + i] = false;
1128
1129 const auto& mask = regs.color_mask[i];
1130 glColorMaski(static_cast<GLuint>(i), mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0);
1131 }
1139} 1132}
1140 1133
1141void RasterizerOpenGL::SyncMultiSampleState() { 1134void RasterizerOpenGL::SyncMultiSampleState() {
1135 auto& gpu = system.GPU().Maxwell3D();
1136 auto& flags = gpu.dirty.flags;
1137 if (!flags[Dirty::MultisampleControl]) {
1138 return;
1139 }
1140 flags[Dirty::MultisampleControl] = false;
1141
1142 const auto& regs = system.GPU().Maxwell3D().regs; 1142 const auto& regs = system.GPU().Maxwell3D().regs;
1143 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0; 1143 oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
1144 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0; 1144 oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
1145} 1145}
1146 1146
1147void RasterizerOpenGL::SyncFragmentColorClampState() { 1147void RasterizerOpenGL::SyncFragmentColorClampState() {
1148 const auto& regs = system.GPU().Maxwell3D().regs; 1148 auto& gpu = system.GPU().Maxwell3D();
1149 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0; 1149 auto& flags = gpu.dirty.flags;
1150 if (!flags[Dirty::FragmentClampColor]) {
1151 return;
1152 }
1153 flags[Dirty::FragmentClampColor] = false;
1154
1155 glClampColor(GL_CLAMP_FRAGMENT_COLOR, gpu.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
1150} 1156}
1151 1157
1152void RasterizerOpenGL::SyncBlendState() { 1158void RasterizerOpenGL::SyncBlendState() {
1153 auto& maxwell3d = system.GPU().Maxwell3D(); 1159 auto& gpu = system.GPU().Maxwell3D();
1154 if (!maxwell3d.dirty.blend_state) { 1160 auto& flags = gpu.dirty.flags;
1161 const auto& regs = gpu.regs;
1162
1163 if (flags[Dirty::BlendColor]) {
1164 flags[Dirty::BlendColor] = false;
1165 glBlendColor(regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
1166 regs.blend_color.a);
1167 }
1168
1169 // TODO(Rodrigo): Revisit blending, there are several registers we are not reading
1170
1171 if (!flags[Dirty::BlendStates]) {
1155 return; 1172 return;
1156 } 1173 }
1157 const auto& regs = maxwell3d.regs; 1174 flags[Dirty::BlendStates] = false;
1158 1175
1159 state.blend_color.red = regs.blend_color.r; 1176 if (!regs.independent_blend_enable) {
1160 state.blend_color.green = regs.blend_color.g; 1177 if (!regs.blend.enable[0]) {
1161 state.blend_color.blue = regs.blend_color.b; 1178 glDisable(GL_BLEND);
1162 state.blend_color.alpha = regs.blend_color.a; 1179 return;
1163
1164 state.independant_blend.enabled = regs.independent_blend_enable;
1165 if (!state.independant_blend.enabled) {
1166 auto& blend = state.blend[0];
1167 const auto& src = regs.blend;
1168 blend.enabled = src.enable[0] != 0;
1169 if (blend.enabled) {
1170 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1171 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1172 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1173 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1174 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1175 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1176 }
1177 for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1178 state.blend[i].enabled = false;
1179 } 1180 }
1180 maxwell3d.dirty.blend_state = false; 1181 glEnable(GL_BLEND);
1181 state.MarkDirtyBlendState(); 1182 glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb),
1183 MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb),
1184 MaxwellToGL::BlendFunc(regs.blend.factor_source_a),
1185 MaxwellToGL::BlendFunc(regs.blend.factor_dest_a));
1186 glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb),
1187 MaxwellToGL::BlendEquation(regs.blend.equation_a));
1182 return; 1188 return;
1183 } 1189 }
1184 1190
1185 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1191 const bool force = flags[Dirty::BlendIndependentEnabled];
1186 auto& blend = state.blend[i]; 1192 flags[Dirty::BlendIndependentEnabled] = false;
1187 const auto& src = regs.independent_blend[i]; 1193
1188 blend.enabled = regs.blend.enable[i] != 0; 1194 for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
1189 if (!blend.enabled) 1195 if (!force && !flags[Dirty::BlendState0 + i]) {
1190 continue; 1196 continue;
1191 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb); 1197 }
1192 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb); 1198 flags[Dirty::BlendState0 + i] = false;
1193 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb); 1199
1194 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a); 1200 if (!regs.blend.enable[i]) {
1195 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a); 1201 glDisablei(GL_BLEND, static_cast<GLuint>(i));
1196 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a); 1202 continue;
1197 } 1203 }
1204 glEnablei(GL_BLEND, static_cast<GLuint>(i));
1198 1205
1199 state.MarkDirtyBlendState(); 1206 const auto& src = regs.independent_blend[i];
1200 maxwell3d.dirty.blend_state = false; 1207 glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.factor_source_rgb),
1208 MaxwellToGL::BlendFunc(src.factor_dest_rgb),
1209 MaxwellToGL::BlendFunc(src.factor_source_a),
1210 MaxwellToGL::BlendFunc(src.factor_dest_a));
1211 glBlendEquationSeparatei(static_cast<GLuint>(i),
1212 MaxwellToGL::BlendEquation(src.equation_rgb),
1213 MaxwellToGL::BlendEquation(src.equation_a));
1214 }
1201} 1215}
1202 1216
1203void RasterizerOpenGL::SyncLogicOpState() { 1217void RasterizerOpenGL::SyncLogicOpState() {
1204 const auto& regs = system.GPU().Maxwell3D().regs; 1218 auto& gpu = system.GPU().Maxwell3D();
1219 auto& flags = gpu.dirty.flags;
1220 if (!flags[Dirty::LogicOp]) {
1221 return;
1222 }
1223 flags[Dirty::LogicOp] = false;
1205 1224
1206 state.logic_op.enabled = regs.logic_op.enable != 0; 1225 const auto& regs = gpu.regs;
1226 if (regs.logic_op.enable) {
1227 glEnable(GL_COLOR_LOGIC_OP);
1228 glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
1229 } else {
1230 glDisable(GL_COLOR_LOGIC_OP);
1231 }
1232}
1207 1233
1208 if (!state.logic_op.enabled) 1234void RasterizerOpenGL::SyncScissorTest() {
1235 auto& gpu = system.GPU().Maxwell3D();
1236 auto& flags = gpu.dirty.flags;
1237 if (!flags[Dirty::Scissors]) {
1209 return; 1238 return;
1239 }
1240 flags[Dirty::Scissors] = false;
1210 1241
1211 ASSERT_MSG(regs.blend.enable[0] == 0, 1242 const auto& regs = gpu.regs;
1212 "Blending and logic op can't be enabled at the same time."); 1243 for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
1213 1244 if (!flags[Dirty::Scissor0 + index]) {
1214 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1245 continue;
1215} 1246 }
1247 flags[Dirty::Scissor0 + index] = false;
1216 1248
1217void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) { 1249 const auto& src = regs.scissor_test[index];
1218 const auto& regs = system.GPU().Maxwell3D().regs; 1250 if (src.enable) {
1219 const bool geometry_shaders_enabled = 1251 glEnablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
1220 regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); 1252 glScissorIndexed(static_cast<GLuint>(index), src.min_x, src.min_y,
1221 const std::size_t viewport_count = 1253 src.max_x - src.min_x, src.max_y - src.min_y);
1222 geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1; 1254 } else {
1223 for (std::size_t i = 0; i < viewport_count; i++) { 1255 glDisablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
1224 const auto& src = regs.scissor_test[i];
1225 auto& dst = current_state.viewports[i].scissor;
1226 dst.enabled = (src.enable != 0);
1227 if (dst.enabled == 0) {
1228 return;
1229 } 1256 }
1230 const u32 width = src.max_x - src.min_x;
1231 const u32 height = src.max_y - src.min_y;
1232 dst.x = src.min_x;
1233 dst.y = src.min_y;
1234 dst.width = width;
1235 dst.height = height;
1236 } 1257 }
1237} 1258}
1238 1259
@@ -1242,45 +1263,78 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1242} 1263}
1243 1264
1244void RasterizerOpenGL::SyncPointState() { 1265void RasterizerOpenGL::SyncPointState() {
1245 const auto& regs = system.GPU().Maxwell3D().regs; 1266 auto& gpu = system.GPU().Maxwell3D();
1267 auto& flags = gpu.dirty.flags;
1268 if (!flags[Dirty::PointSize]) {
1269 return;
1270 }
1271 flags[Dirty::PointSize] = false;
1272
1273 oglEnable(GL_POINT_SPRITE, gpu.regs.point_sprite_enable);
1274
1275 if (gpu.regs.vp_point_size.enable) {
1276 // By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled.
1277 glEnable(GL_PROGRAM_POINT_SIZE);
1278 return;
1279 }
1280
1246 // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid 1281 // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
1247 // in OpenGL). 1282 // in OpenGL).
1248 state.point.program_control = regs.vp_point_size.enable != 0; 1283 glPointSize(std::max(1.0f, gpu.regs.point_size));
1249 state.point.sprite = regs.point_sprite_enable != 0; 1284 glDisable(GL_PROGRAM_POINT_SIZE);
1250 state.point.size = std::max(1.0f, regs.point_size);
1251} 1285}
1252 1286
1253void RasterizerOpenGL::SyncPolygonOffset() { 1287void RasterizerOpenGL::SyncPolygonOffset() {
1254 auto& maxwell3d = system.GPU().Maxwell3D(); 1288 auto& gpu = system.GPU().Maxwell3D();
1255 if (!maxwell3d.dirty.polygon_offset) { 1289 auto& flags = gpu.dirty.flags;
1290 if (!flags[Dirty::PolygonOffset]) {
1256 return; 1291 return;
1257 } 1292 }
1258 const auto& regs = maxwell3d.regs; 1293 flags[Dirty::PolygonOffset] = false;
1259
1260 state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
1261 state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
1262 state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
1263 1294
1264 // Hardware divides polygon offset units by two 1295 const auto& regs = gpu.regs;
1265 state.polygon_offset.units = regs.polygon_offset_units / 2.0f; 1296 oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
1266 state.polygon_offset.factor = regs.polygon_offset_factor; 1297 oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
1267 state.polygon_offset.clamp = regs.polygon_offset_clamp; 1298 oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
1268 1299
1269 state.MarkDirtyPolygonOffset(); 1300 if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable ||
1270 maxwell3d.dirty.polygon_offset = false; 1301 regs.polygon_offset_point_enable) {
1302 // Hardware divides polygon offset units by two
1303 glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f,
1304 regs.polygon_offset_clamp);
1305 }
1271} 1306}
1272 1307
1273void RasterizerOpenGL::SyncAlphaTest() { 1308void RasterizerOpenGL::SyncAlphaTest() {
1274 const auto& regs = system.GPU().Maxwell3D().regs; 1309 auto& gpu = system.GPU().Maxwell3D();
1275 UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1, 1310 auto& flags = gpu.dirty.flags;
1276 "Alpha Testing is enabled with more than one rendertarget"); 1311 if (!flags[Dirty::AlphaTest]) {
1312 return;
1313 }
1314 flags[Dirty::AlphaTest] = false;
1315
1316 const auto& regs = gpu.regs;
1317 if (regs.alpha_test_enabled && regs.rt_control.count > 1) {
1318 LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested");
1319 }
1277 1320
1278 state.alpha_test.enabled = regs.alpha_test_enabled; 1321 if (regs.alpha_test_enabled) {
1279 if (!state.alpha_test.enabled) { 1322 glEnable(GL_ALPHA_TEST);
1323 glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
1324 } else {
1325 glDisable(GL_ALPHA_TEST);
1326 }
1327}
1328
1329void RasterizerOpenGL::SyncFramebufferSRGB() {
1330 auto& gpu = system.GPU().Maxwell3D();
1331 auto& flags = gpu.dirty.flags;
1332 if (!flags[Dirty::FramebufferSRGB]) {
1280 return; 1333 return;
1281 } 1334 }
1282 state.alpha_test.func = MaxwellToGL::ComparisonOp(regs.alpha_test_func); 1335 flags[Dirty::FramebufferSRGB] = false;
1283 state.alpha_test.ref = regs.alpha_test_ref; 1336
1337 oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
1284} 1338}
1285 1339
1286} // namespace OpenGL 1340} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 68abe9a21..b24c6661b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -30,7 +30,7 @@
30#include "video_core/renderer_opengl/gl_shader_cache.h" 30#include "video_core/renderer_opengl/gl_shader_cache.h"
31#include "video_core/renderer_opengl/gl_shader_decompiler.h" 31#include "video_core/renderer_opengl/gl_shader_decompiler.h"
32#include "video_core/renderer_opengl/gl_shader_manager.h" 32#include "video_core/renderer_opengl/gl_shader_manager.h"
33#include "video_core/renderer_opengl/gl_state.h" 33#include "video_core/renderer_opengl/gl_state_tracker.h"
34#include "video_core/renderer_opengl/gl_texture_cache.h" 34#include "video_core/renderer_opengl/gl_texture_cache.h"
35#include "video_core/renderer_opengl/utils.h" 35#include "video_core/renderer_opengl/utils.h"
36#include "video_core/textures/texture.h" 36#include "video_core/textures/texture.h"
@@ -55,7 +55,8 @@ struct DrawParameters;
55class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { 55class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
56public: 56public:
57 explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, 57 explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
58 ScreenInfo& info); 58 ScreenInfo& info, GLShader::ProgramManager& program_manager,
59 StateTracker& state_tracker);
59 ~RasterizerOpenGL() override; 60 ~RasterizerOpenGL() override;
60 61
61 void Draw(bool is_indexed, bool is_instanced) override; 62 void Draw(bool is_indexed, bool is_instanced) override;
@@ -76,6 +77,7 @@ public:
76 u32 pixel_stride) override; 77 u32 pixel_stride) override;
77 void LoadDiskResources(const std::atomic_bool& stop_loading, 78 void LoadDiskResources(const std::atomic_bool& stop_loading,
78 const VideoCore::DiskResourceLoadCallback& callback) override; 79 const VideoCore::DiskResourceLoadCallback& callback) override;
80 void SetupDirtyFlags() override;
79 81
80 /// Returns true when there are commands queued to the OpenGL server. 82 /// Returns true when there are commands queued to the OpenGL server.
81 bool AnyCommandQueued() const { 83 bool AnyCommandQueued() const {
@@ -86,8 +88,7 @@ private:
86 /// Configures the color and depth framebuffer states. 88 /// Configures the color and depth framebuffer states.
87 void ConfigureFramebuffers(); 89 void ConfigureFramebuffers();
88 90
89 void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, 91 void ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb, bool using_stencil_fb);
90 bool using_depth_fb, bool using_stencil_fb);
91 92
92 /// Configures the current constbuffers to use for the draw command. 93 /// Configures the current constbuffers to use for the draw command.
93 void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader); 94 void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader);
@@ -130,11 +131,13 @@ private:
130 const GLShader::ImageEntry& entry); 131 const GLShader::ImageEntry& entry);
131 132
132 /// Syncs the viewport and depth range to match the guest state 133 /// Syncs the viewport and depth range to match the guest state
133 void SyncViewport(OpenGLState& current_state); 134 void SyncViewport();
135
136 /// Syncs the depth clamp state
137 void SyncDepthClamp();
134 138
135 /// Syncs the clip enabled status to match the guest state 139 /// Syncs the clip enabled status to match the guest state
136 void SyncClipEnabled( 140 void SyncClipEnabled(u32 clip_mask);
137 const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
138 141
139 /// Syncs the clip coefficients to match the guest state 142 /// Syncs the clip coefficients to match the guest state
140 void SyncClipCoef(); 143 void SyncClipCoef();
@@ -164,7 +167,7 @@ private:
164 void SyncMultiSampleState(); 167 void SyncMultiSampleState();
165 168
166 /// Syncs the scissor test state to match the guest state 169 /// Syncs the scissor test state to match the guest state
167 void SyncScissorTest(OpenGLState& current_state); 170 void SyncScissorTest();
168 171
169 /// Syncs the transform feedback state to match the guest state 172 /// Syncs the transform feedback state to match the guest state
170 void SyncTransformFeedback(); 173 void SyncTransformFeedback();
@@ -173,7 +176,7 @@ private:
173 void SyncPointState(); 176 void SyncPointState();
174 177
175 /// Syncs the rasterizer enable state to match the guest state 178 /// Syncs the rasterizer enable state to match the guest state
176 void SyncRasterizeEnable(OpenGLState& current_state); 179 void SyncRasterizeEnable();
177 180
178 /// Syncs Color Mask 181 /// Syncs Color Mask
179 void SyncColorMask(); 182 void SyncColorMask();
@@ -184,6 +187,9 @@ private:
184 /// Syncs the alpha test state to match the guest state 187 /// Syncs the alpha test state to match the guest state
185 void SyncAlphaTest(); 188 void SyncAlphaTest();
186 189
190 /// Syncs the framebuffer sRGB state to match the guest state
191 void SyncFramebufferSRGB();
192
187 /// Check for extension that are not strictly required but are needed for correct emulation 193 /// Check for extension that are not strictly required but are needed for correct emulation
188 void CheckExtensions(); 194 void CheckExtensions();
189 195
@@ -191,18 +197,17 @@ private:
191 197
192 std::size_t CalculateIndexBufferSize() const; 198 std::size_t CalculateIndexBufferSize() const;
193 199
194 /// Updates and returns a vertex array object representing current vertex format 200 /// Updates the current vertex format
195 GLuint SetupVertexFormat(); 201 void SetupVertexFormat();
196 202
197 void SetupVertexBuffer(GLuint vao); 203 void SetupVertexBuffer();
198 void SetupVertexInstances(GLuint vao); 204 void SetupVertexInstances();
199 205
200 GLintptr SetupIndexBuffer(); 206 GLintptr SetupIndexBuffer();
201 207
202 void SetupShaders(GLenum primitive_mode); 208 void SetupShaders(GLenum primitive_mode);
203 209
204 const Device device; 210 const Device device;
205 OpenGLState state;
206 211
207 TextureCacheOpenGL texture_cache; 212 TextureCacheOpenGL texture_cache;
208 ShaderCacheOpenGL shader_cache; 213 ShaderCacheOpenGL shader_cache;
@@ -212,22 +217,20 @@ private:
212 217
213 Core::System& system; 218 Core::System& system;
214 ScreenInfo& screen_info; 219 ScreenInfo& screen_info;
215 220 GLShader::ProgramManager& program_manager;
216 std::unique_ptr<GLShader::ProgramManager> shader_program_manager; 221 StateTracker& state_tracker;
217 std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
218 Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
219 OGLVertexArray>
220 vertex_array_cache;
221 222
222 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 223 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
223 OGLBufferCache buffer_cache; 224 OGLBufferCache buffer_cache;
224 225
225 VertexArrayPushBuffer vertex_array_pushbuffer; 226 VertexArrayPushBuffer vertex_array_pushbuffer{state_tracker};
226 BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; 227 BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
227 BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; 228 BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
228 229
229 /// Number of commands queued to the OpenGL driver. Reseted on flush. 230 /// Number of commands queued to the OpenGL driver. Reseted on flush.
230 std::size_t num_queued_commands = 0; 231 std::size_t num_queued_commands = 0;
232
233 u32 last_clip_distance_mask = 0;
231}; 234};
232 235
233} // namespace OpenGL 236} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index c0aee770f..97803d480 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -8,7 +8,6 @@
8#include "common/microprofile.h" 8#include "common/microprofile.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h" 9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/renderer_opengl/gl_shader_util.h" 10#include "video_core/renderer_opengl/gl_shader_util.h"
11#include "video_core/renderer_opengl/gl_state.h"
12 11
13MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192)); 12MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
14MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192)); 13MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
@@ -20,7 +19,7 @@ void OGLRenderbuffer::Create() {
20 return; 19 return;
21 20
22 MICROPROFILE_SCOPE(OpenGL_ResourceCreation); 21 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
23 glGenRenderbuffers(1, &handle); 22 glCreateRenderbuffers(1, &handle);
24} 23}
25 24
26void OGLRenderbuffer::Release() { 25void OGLRenderbuffer::Release() {
@@ -29,7 +28,6 @@ void OGLRenderbuffer::Release() {
29 28
30 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 29 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
31 glDeleteRenderbuffers(1, &handle); 30 glDeleteRenderbuffers(1, &handle);
32 OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply();
33 handle = 0; 31 handle = 0;
34} 32}
35 33
@@ -47,7 +45,6 @@ void OGLTexture::Release() {
47 45
48 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 46 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
49 glDeleteTextures(1, &handle); 47 glDeleteTextures(1, &handle);
50 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
51 handle = 0; 48 handle = 0;
52} 49}
53 50
@@ -65,7 +62,6 @@ void OGLTextureView::Release() {
65 62
66 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 63 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
67 glDeleteTextures(1, &handle); 64 glDeleteTextures(1, &handle);
68 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
69 handle = 0; 65 handle = 0;
70} 66}
71 67
@@ -83,7 +79,6 @@ void OGLSampler::Release() {
83 79
84 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 80 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
85 glDeleteSamplers(1, &handle); 81 glDeleteSamplers(1, &handle);
86 OpenGLState::GetCurState().ResetSampler(handle).Apply();
87 handle = 0; 82 handle = 0;
88} 83}
89 84
@@ -127,7 +122,6 @@ void OGLProgram::Release() {
127 122
128 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 123 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
129 glDeleteProgram(handle); 124 glDeleteProgram(handle);
130 OpenGLState::GetCurState().ResetProgram(handle).Apply();
131 handle = 0; 125 handle = 0;
132} 126}
133 127
@@ -145,7 +139,6 @@ void OGLPipeline::Release() {
145 139
146 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 140 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
147 glDeleteProgramPipelines(1, &handle); 141 glDeleteProgramPipelines(1, &handle);
148 OpenGLState::GetCurState().ResetPipeline(handle).Apply();
149 handle = 0; 142 handle = 0;
150} 143}
151 144
@@ -189,24 +182,6 @@ void OGLSync::Release() {
189 handle = 0; 182 handle = 0;
190} 183}
191 184
192void OGLVertexArray::Create() {
193 if (handle != 0)
194 return;
195
196 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
197 glCreateVertexArrays(1, &handle);
198}
199
200void OGLVertexArray::Release() {
201 if (handle == 0)
202 return;
203
204 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
205 glDeleteVertexArrays(1, &handle);
206 OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
207 handle = 0;
208}
209
210void OGLFramebuffer::Create() { 185void OGLFramebuffer::Create() {
211 if (handle != 0) 186 if (handle != 0)
212 return; 187 return;
@@ -221,7 +196,6 @@ void OGLFramebuffer::Release() {
221 196
222 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); 197 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
223 glDeleteFramebuffers(1, &handle); 198 glDeleteFramebuffers(1, &handle);
224 OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
225 handle = 0; 199 handle = 0;
226} 200}
227 201
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 995a4e45e..de93f4212 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -241,31 +241,6 @@ public:
241 GLsync handle = 0; 241 GLsync handle = 0;
242}; 242};
243 243
244class OGLVertexArray : private NonCopyable {
245public:
246 OGLVertexArray() = default;
247
248 OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
249
250 ~OGLVertexArray() {
251 Release();
252 }
253
254 OGLVertexArray& operator=(OGLVertexArray&& o) noexcept {
255 Release();
256 handle = std::exchange(o.handle, 0);
257 return *this;
258 }
259
260 /// Creates a new internal OpenGL resource and stores the handle
261 void Create();
262
263 /// Deletes the internal OpenGL resource
264 void Release();
265
266 GLuint handle = 0;
267};
268
269class OGLFramebuffer : private NonCopyable { 244class OGLFramebuffer : private NonCopyable {
270public: 245public:
271 OGLFramebuffer() = default; 246 OGLFramebuffer() = default;
diff --git a/src/video_core/renderer_opengl/gl_sampler_cache.cpp b/src/video_core/renderer_opengl/gl_sampler_cache.cpp
index 3ded5ecea..5c174879a 100644
--- a/src/video_core/renderer_opengl/gl_sampler_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_sampler_cache.cpp
@@ -38,7 +38,7 @@ OGLSampler SamplerCacheOpenGL::CreateSampler(const Tegra::Texture::TSCEntry& tsc
38 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, tsc.GetMaxAnisotropy()); 38 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, tsc.GetMaxAnisotropy());
39 } else if (GLAD_GL_EXT_texture_filter_anisotropic) { 39 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
40 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, tsc.GetMaxAnisotropy()); 40 glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, tsc.GetMaxAnisotropy());
41 } else if (tsc.GetMaxAnisotropy() != 1) { 41 } else {
42 LOG_WARNING(Render_OpenGL, "Anisotropy not supported by host GPU driver"); 42 LOG_WARNING(Render_OpenGL, "Anisotropy not supported by host GPU driver");
43 } 43 }
44 44
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 489eb143c..4cb89db8c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
22#include "video_core/renderer_opengl/gl_shader_cache.h" 22#include "video_core/renderer_opengl/gl_shader_cache.h"
23#include "video_core/renderer_opengl/gl_shader_decompiler.h" 23#include "video_core/renderer_opengl/gl_shader_decompiler.h"
24#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 24#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
25#include "video_core/renderer_opengl/gl_state_tracker.h"
25#include "video_core/renderer_opengl/utils.h" 26#include "video_core/renderer_opengl/utils.h"
26#include "video_core/shader/shader_ir.h" 27#include "video_core/shader/shader_ir.h"
27 28
@@ -623,7 +624,7 @@ bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
623} 624}
624 625
625Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 626Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
626 if (!system.GPU().Maxwell3D().dirty.shaders) { 627 if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
627 return last_shaders[static_cast<std::size_t>(program)]; 628 return last_shaders[static_cast<std::size_t>(program)];
628 } 629 }
629 630
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4735000b5..3a41ed30c 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -2547,7 +2547,10 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
2547 for (const auto& image : ir.GetImages()) { 2547 for (const auto& image : ir.GetImages()) {
2548 entries.images.emplace_back(image); 2548 entries.images.emplace_back(image);
2549 } 2549 }
2550 entries.clip_distances = ir.GetClipDistances(); 2550 const auto clip_distances = ir.GetClipDistances();
2551 for (std::size_t i = 0; i < std::size(clip_distances); ++i) {
2552 entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i;
2553 }
2551 entries.shader_length = ir.GetLength(); 2554 entries.shader_length = ir.GetLength();
2552 return entries; 2555 return entries;
2553} 2556}
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 7876f48d6..0f692c1db 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -74,7 +74,7 @@ struct ShaderEntries {
74 std::vector<GlobalMemoryEntry> global_memory_entries; 74 std::vector<GlobalMemoryEntry> global_memory_entries;
75 std::vector<SamplerEntry> samplers; 75 std::vector<SamplerEntry> samplers;
76 std::vector<ImageEntry> images; 76 std::vector<ImageEntry> images;
77 std::array<bool, Maxwell::NumClipDistances> clip_distances{}; 77 u32 clip_distances{};
78 std::size_t shader_length{}; 78 std::size_t shader_length{};
79}; 79};
80 80
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 75d3fac04..9c7b0adbd 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -2,45 +2,52 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <glad/glad.h>
6
5#include "common/common_types.h" 7#include "common/common_types.h"
6#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
7#include "video_core/renderer_opengl/gl_shader_manager.h" 9#include "video_core/renderer_opengl/gl_shader_manager.h"
8 10
9namespace OpenGL::GLShader { 11namespace OpenGL::GLShader {
10 12
11using Tegra::Engines::Maxwell3D; 13ProgramManager::ProgramManager() = default;
12
13ProgramManager::ProgramManager() {
14 pipeline.Create();
15}
16 14
17ProgramManager::~ProgramManager() = default; 15ProgramManager::~ProgramManager() = default;
18 16
19void ProgramManager::ApplyTo(OpenGLState& state) { 17void ProgramManager::Create() {
20 UpdatePipeline(); 18 graphics_pipeline.Create();
21 state.draw.shader_program = 0; 19 glBindProgramPipeline(graphics_pipeline.handle);
22 state.draw.program_pipeline = pipeline.handle;
23} 20}
24 21
25void ProgramManager::UpdatePipeline() { 22void ProgramManager::BindGraphicsPipeline() {
23 if (!is_graphics_bound) {
24 is_graphics_bound = true;
25 glUseProgram(0);
26 }
27
26 // Avoid updating the pipeline when values have no changed 28 // Avoid updating the pipeline when values have no changed
27 if (old_state == current_state) { 29 if (old_state == current_state) {
28 return; 30 return;
29 } 31 }
30 32
31 // Workaround for AMD bug 33 // Workaround for AMD bug
32 constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | 34 static constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
33 GL_FRAGMENT_SHADER_BIT}; 35 GL_FRAGMENT_SHADER_BIT};
34 glUseProgramStages(pipeline.handle, all_used_stages, 0); 36 const GLuint handle = graphics_pipeline.handle;
35 37 glUseProgramStages(handle, all_used_stages, 0);
36 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader); 38 glUseProgramStages(handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader);
37 glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader); 39 glUseProgramStages(handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader);
38 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader); 40 glUseProgramStages(handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader);
39 41
40 old_state = current_state; 42 old_state = current_state;
41} 43}
42 44
43void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell) { 45void ProgramManager::BindComputeShader(GLuint program) {
46 is_graphics_bound = false;
47 glUseProgram(program);
48}
49
50void MaxwellUniformData::SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell) {
44 const auto& regs = maxwell.regs; 51 const auto& regs = maxwell.regs;
45 52
46 // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value. 53 // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 478c165ce..d2e47f2a9 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -9,7 +9,6 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10 10
11#include "video_core/renderer_opengl/gl_resource_manager.h" 11#include "video_core/renderer_opengl/gl_resource_manager.h"
12#include "video_core/renderer_opengl/gl_state.h"
13#include "video_core/renderer_opengl/maxwell_to_gl.h" 12#include "video_core/renderer_opengl/maxwell_to_gl.h"
14 13
15namespace OpenGL::GLShader { 14namespace OpenGL::GLShader {
@@ -32,49 +31,47 @@ public:
32 explicit ProgramManager(); 31 explicit ProgramManager();
33 ~ProgramManager(); 32 ~ProgramManager();
34 33
35 void ApplyTo(OpenGLState& state); 34 void Create();
36 35
37 void UseProgrammableVertexShader(GLuint program) { 36 /// Updates the graphics pipeline and binds it.
37 void BindGraphicsPipeline();
38
39 /// Binds a compute shader.
40 void BindComputeShader(GLuint program);
41
42 void UseVertexShader(GLuint program) {
38 current_state.vertex_shader = program; 43 current_state.vertex_shader = program;
39 } 44 }
40 45
41 void UseProgrammableGeometryShader(GLuint program) { 46 void UseGeometryShader(GLuint program) {
42 current_state.geometry_shader = program; 47 current_state.geometry_shader = program;
43 } 48 }
44 49
45 void UseProgrammableFragmentShader(GLuint program) { 50 void UseFragmentShader(GLuint program) {
46 current_state.fragment_shader = program; 51 current_state.fragment_shader = program;
47 } 52 }
48 53
49 void UseTrivialGeometryShader() {
50 current_state.geometry_shader = 0;
51 }
52
53 void UseTrivialFragmentShader() {
54 current_state.fragment_shader = 0;
55 }
56
57private: 54private:
58 struct PipelineState { 55 struct PipelineState {
59 bool operator==(const PipelineState& rhs) const { 56 bool operator==(const PipelineState& rhs) const noexcept {
60 return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader && 57 return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader &&
61 geometry_shader == rhs.geometry_shader; 58 geometry_shader == rhs.geometry_shader;
62 } 59 }
63 60
64 bool operator!=(const PipelineState& rhs) const { 61 bool operator!=(const PipelineState& rhs) const noexcept {
65 return !operator==(rhs); 62 return !operator==(rhs);
66 } 63 }
67 64
68 GLuint vertex_shader{}; 65 GLuint vertex_shader = 0;
69 GLuint fragment_shader{}; 66 GLuint fragment_shader = 0;
70 GLuint geometry_shader{}; 67 GLuint geometry_shader = 0;
71 }; 68 };
72 69
73 void UpdatePipeline(); 70 OGLPipeline graphics_pipeline;
74 71 OGLPipeline compute_pipeline;
75 OGLPipeline pipeline;
76 PipelineState current_state; 72 PipelineState current_state;
77 PipelineState old_state; 73 PipelineState old_state;
74 bool is_graphics_bound = true;
78}; 75};
79 76
80} // namespace OpenGL::GLShader 77} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
deleted file mode 100644
index 7d3bc1a1f..000000000
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ /dev/null
@@ -1,569 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <iterator>
7#include <glad/glad.h>
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "common/microprofile.h"
11#include "video_core/renderer_opengl/gl_state.h"
12
13MICROPROFILE_DEFINE(OpenGL_State, "OpenGL", "State Change", MP_RGB(192, 128, 128));
14
15namespace OpenGL {
16
17using Maxwell = Tegra::Engines::Maxwell3D::Regs;
18
19OpenGLState OpenGLState::cur_state;
20
21namespace {
22
23template <typename T>
24bool UpdateValue(T& current_value, const T new_value) {
25 const bool changed = current_value != new_value;
26 current_value = new_value;
27 return changed;
28}
29
30template <typename T1, typename T2>
31bool UpdateTie(T1 current_value, const T2 new_value) {
32 const bool changed = current_value != new_value;
33 current_value = new_value;
34 return changed;
35}
36
37template <typename T>
38std::optional<std::pair<GLuint, GLsizei>> UpdateArray(T& current_values, const T& new_values) {
39 std::optional<std::size_t> first;
40 std::size_t last;
41 for (std::size_t i = 0; i < std::size(current_values); ++i) {
42 if (!UpdateValue(current_values[i], new_values[i])) {
43 continue;
44 }
45 if (!first) {
46 first = i;
47 }
48 last = i;
49 }
50 if (!first) {
51 return std::nullopt;
52 }
53 return std::make_pair(static_cast<GLuint>(*first), static_cast<GLsizei>(last - *first + 1));
54}
55
56void Enable(GLenum cap, bool enable) {
57 if (enable) {
58 glEnable(cap);
59 } else {
60 glDisable(cap);
61 }
62}
63
64void Enable(GLenum cap, GLuint index, bool enable) {
65 if (enable) {
66 glEnablei(cap, index);
67 } else {
68 glDisablei(cap, index);
69 }
70}
71
72void Enable(GLenum cap, bool& current_value, bool new_value) {
73 if (UpdateValue(current_value, new_value)) {
74 Enable(cap, new_value);
75 }
76}
77
78void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
79 if (UpdateValue(current_value, new_value)) {
80 Enable(cap, index, new_value);
81 }
82}
83
84} // Anonymous namespace
85
86OpenGLState::OpenGLState() = default;
87
88void OpenGLState::SetDefaultViewports() {
89 viewports.fill(Viewport{});
90
91 depth_clamp.far_plane = false;
92 depth_clamp.near_plane = false;
93}
94
95void OpenGLState::ApplyFramebufferState() {
96 if (UpdateValue(cur_state.draw.read_framebuffer, draw.read_framebuffer)) {
97 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
98 }
99 if (UpdateValue(cur_state.draw.draw_framebuffer, draw.draw_framebuffer)) {
100 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
101 }
102}
103
104void OpenGLState::ApplyVertexArrayState() {
105 if (UpdateValue(cur_state.draw.vertex_array, draw.vertex_array)) {
106 glBindVertexArray(draw.vertex_array);
107 }
108}
109
110void OpenGLState::ApplyShaderProgram() {
111 if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) {
112 glUseProgram(draw.shader_program);
113 }
114}
115
116void OpenGLState::ApplyProgramPipeline() {
117 if (UpdateValue(cur_state.draw.program_pipeline, draw.program_pipeline)) {
118 glBindProgramPipeline(draw.program_pipeline);
119 }
120}
121
122void OpenGLState::ApplyClipDistances() {
123 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
124 Enable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i), cur_state.clip_distance[i],
125 clip_distance[i]);
126 }
127}
128
129void OpenGLState::ApplyPointSize() {
130 Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control);
131 Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite);
132 if (UpdateValue(cur_state.point.size, point.size)) {
133 glPointSize(point.size);
134 }
135}
136
137void OpenGLState::ApplyFragmentColorClamp() {
138 if (UpdateValue(cur_state.fragment_color_clamp.enabled, fragment_color_clamp.enabled)) {
139 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
140 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
141 }
142}
143
144void OpenGLState::ApplyMultisample() {
145 Enable(GL_SAMPLE_ALPHA_TO_COVERAGE, cur_state.multisample_control.alpha_to_coverage,
146 multisample_control.alpha_to_coverage);
147 Enable(GL_SAMPLE_ALPHA_TO_ONE, cur_state.multisample_control.alpha_to_one,
148 multisample_control.alpha_to_one);
149}
150
151void OpenGLState::ApplyDepthClamp() {
152 if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane &&
153 depth_clamp.near_plane == cur_state.depth_clamp.near_plane) {
154 return;
155 }
156 cur_state.depth_clamp = depth_clamp;
157
158 UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane,
159 "Unimplemented Depth Clamp Separation!");
160
161 Enable(GL_DEPTH_CLAMP, depth_clamp.far_plane || depth_clamp.near_plane);
162}
163
164void OpenGLState::ApplySRgb() {
165 if (cur_state.framebuffer_srgb.enabled == framebuffer_srgb.enabled)
166 return;
167 cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
168 if (framebuffer_srgb.enabled) {
169 glEnable(GL_FRAMEBUFFER_SRGB);
170 } else {
171 glDisable(GL_FRAMEBUFFER_SRGB);
172 }
173}
174
175void OpenGLState::ApplyCulling() {
176 Enable(GL_CULL_FACE, cur_state.cull.enabled, cull.enabled);
177
178 if (UpdateValue(cur_state.cull.mode, cull.mode)) {
179 glCullFace(cull.mode);
180 }
181
182 if (UpdateValue(cur_state.cull.front_face, cull.front_face)) {
183 glFrontFace(cull.front_face);
184 }
185}
186
187void OpenGLState::ApplyRasterizerDiscard() {
188 Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
189}
190
191void OpenGLState::ApplyColorMask() {
192 if (!dirty.color_mask) {
193 return;
194 }
195 dirty.color_mask = false;
196
197 for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
198 const auto& updated = color_mask[i];
199 auto& current = cur_state.color_mask[i];
200 if (updated.red_enabled != current.red_enabled ||
201 updated.green_enabled != current.green_enabled ||
202 updated.blue_enabled != current.blue_enabled ||
203 updated.alpha_enabled != current.alpha_enabled) {
204 current = updated;
205 glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
206 updated.blue_enabled, updated.alpha_enabled);
207 }
208 }
209}
210
211void OpenGLState::ApplyDepth() {
212 Enable(GL_DEPTH_TEST, cur_state.depth.test_enabled, depth.test_enabled);
213
214 if (cur_state.depth.test_func != depth.test_func) {
215 cur_state.depth.test_func = depth.test_func;
216 glDepthFunc(depth.test_func);
217 }
218
219 if (cur_state.depth.write_mask != depth.write_mask) {
220 cur_state.depth.write_mask = depth.write_mask;
221 glDepthMask(depth.write_mask);
222 }
223}
224
225void OpenGLState::ApplyPrimitiveRestart() {
226 Enable(GL_PRIMITIVE_RESTART, cur_state.primitive_restart.enabled, primitive_restart.enabled);
227
228 if (cur_state.primitive_restart.index != primitive_restart.index) {
229 cur_state.primitive_restart.index = primitive_restart.index;
230 glPrimitiveRestartIndex(primitive_restart.index);
231 }
232}
233
234void OpenGLState::ApplyStencilTest() {
235 if (!dirty.stencil_state) {
236 return;
237 }
238 dirty.stencil_state = false;
239
240 Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
241
242 const auto ConfigStencil = [](GLenum face, const auto& config, auto& current) {
243 if (current.test_func != config.test_func || current.test_ref != config.test_ref ||
244 current.test_mask != config.test_mask) {
245 current.test_func = config.test_func;
246 current.test_ref = config.test_ref;
247 current.test_mask = config.test_mask;
248 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
249 }
250 if (current.action_depth_fail != config.action_depth_fail ||
251 current.action_depth_pass != config.action_depth_pass ||
252 current.action_stencil_fail != config.action_stencil_fail) {
253 current.action_depth_fail = config.action_depth_fail;
254 current.action_depth_pass = config.action_depth_pass;
255 current.action_stencil_fail = config.action_stencil_fail;
256 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
257 config.action_depth_pass);
258 }
259 if (current.write_mask != config.write_mask) {
260 current.write_mask = config.write_mask;
261 glStencilMaskSeparate(face, config.write_mask);
262 }
263 };
264 ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front);
265 ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back);
266}
267
268void OpenGLState::ApplyViewport() {
269 for (GLuint i = 0; i < static_cast<GLuint>(Maxwell::NumViewports); ++i) {
270 const auto& updated = viewports[i];
271 auto& current = cur_state.viewports[i];
272
273 if (current.x != updated.x || current.y != updated.y || current.width != updated.width ||
274 current.height != updated.height) {
275 current.x = updated.x;
276 current.y = updated.y;
277 current.width = updated.width;
278 current.height = updated.height;
279 glViewportIndexedf(i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
280 static_cast<GLfloat>(updated.width),
281 static_cast<GLfloat>(updated.height));
282 }
283 if (current.depth_range_near != updated.depth_range_near ||
284 current.depth_range_far != updated.depth_range_far) {
285 current.depth_range_near = updated.depth_range_near;
286 current.depth_range_far = updated.depth_range_far;
287 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
288 }
289
290 Enable(GL_SCISSOR_TEST, i, current.scissor.enabled, updated.scissor.enabled);
291
292 if (current.scissor.x != updated.scissor.x || current.scissor.y != updated.scissor.y ||
293 current.scissor.width != updated.scissor.width ||
294 current.scissor.height != updated.scissor.height) {
295 current.scissor.x = updated.scissor.x;
296 current.scissor.y = updated.scissor.y;
297 current.scissor.width = updated.scissor.width;
298 current.scissor.height = updated.scissor.height;
299 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
300 updated.scissor.height);
301 }
302 }
303}
304
305void OpenGLState::ApplyGlobalBlending() {
306 const Blend& updated = blend[0];
307 Blend& current = cur_state.blend[0];
308
309 Enable(GL_BLEND, current.enabled, updated.enabled);
310
311 if (current.src_rgb_func != updated.src_rgb_func ||
312 current.dst_rgb_func != updated.dst_rgb_func || current.src_a_func != updated.src_a_func ||
313 current.dst_a_func != updated.dst_a_func) {
314 current.src_rgb_func = updated.src_rgb_func;
315 current.dst_rgb_func = updated.dst_rgb_func;
316 current.src_a_func = updated.src_a_func;
317 current.dst_a_func = updated.dst_a_func;
318 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
319 updated.dst_a_func);
320 }
321
322 if (current.rgb_equation != updated.rgb_equation || current.a_equation != updated.a_equation) {
323 current.rgb_equation = updated.rgb_equation;
324 current.a_equation = updated.a_equation;
325 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
326 }
327}
328
329void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) {
330 const Blend& updated = blend[target];
331 Blend& current = cur_state.blend[target];
332
333 if (current.enabled != updated.enabled || force) {
334 current.enabled = updated.enabled;
335 Enable(GL_BLEND, static_cast<GLuint>(target), updated.enabled);
336 }
337
338 if (UpdateTie(std::tie(current.src_rgb_func, current.dst_rgb_func, current.src_a_func,
339 current.dst_a_func),
340 std::tie(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
341 updated.dst_a_func))) {
342 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
343 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
344 }
345
346 if (UpdateTie(std::tie(current.rgb_equation, current.a_equation),
347 std::tie(updated.rgb_equation, updated.a_equation))) {
348 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
349 updated.a_equation);
350 }
351}
352
353void OpenGLState::ApplyBlending() {
354 if (!dirty.blend_state) {
355 return;
356 }
357 dirty.blend_state = false;
358
359 if (independant_blend.enabled) {
360 const bool force = independant_blend.enabled != cur_state.independant_blend.enabled;
361 for (std::size_t target = 0; target < Maxwell::NumRenderTargets; ++target) {
362 ApplyTargetBlending(target, force);
363 }
364 } else {
365 ApplyGlobalBlending();
366 }
367 cur_state.independant_blend.enabled = independant_blend.enabled;
368
369 if (UpdateTie(
370 std::tie(cur_state.blend_color.red, cur_state.blend_color.green,
371 cur_state.blend_color.blue, cur_state.blend_color.alpha),
372 std::tie(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha))) {
373 glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha);
374 }
375}
376
377void OpenGLState::ApplyLogicOp() {
378 Enable(GL_COLOR_LOGIC_OP, cur_state.logic_op.enabled, logic_op.enabled);
379
380 if (UpdateValue(cur_state.logic_op.operation, logic_op.operation)) {
381 glLogicOp(logic_op.operation);
382 }
383}
384
385void OpenGLState::ApplyPolygonOffset() {
386 if (!dirty.polygon_offset) {
387 return;
388 }
389 dirty.polygon_offset = false;
390
391 Enable(GL_POLYGON_OFFSET_FILL, cur_state.polygon_offset.fill_enable,
392 polygon_offset.fill_enable);
393 Enable(GL_POLYGON_OFFSET_LINE, cur_state.polygon_offset.line_enable,
394 polygon_offset.line_enable);
395 Enable(GL_POLYGON_OFFSET_POINT, cur_state.polygon_offset.point_enable,
396 polygon_offset.point_enable);
397
398 if (UpdateTie(std::tie(cur_state.polygon_offset.factor, cur_state.polygon_offset.units,
399 cur_state.polygon_offset.clamp),
400 std::tie(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp))) {
401 if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
402 glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
403 } else {
404 UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0,
405 "Unimplemented Depth polygon offset clamp.");
406 glPolygonOffset(polygon_offset.factor, polygon_offset.units);
407 }
408 }
409}
410
411void OpenGLState::ApplyAlphaTest() {
412 Enable(GL_ALPHA_TEST, cur_state.alpha_test.enabled, alpha_test.enabled);
413 if (UpdateTie(std::tie(cur_state.alpha_test.func, cur_state.alpha_test.ref),
414 std::tie(alpha_test.func, alpha_test.ref))) {
415 glAlphaFunc(alpha_test.func, alpha_test.ref);
416 }
417}
418
419void OpenGLState::ApplyClipControl() {
420 if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
421 std::tie(clip_control.origin, clip_control.depth_mode))) {
422 glClipControl(clip_control.origin, clip_control.depth_mode);
423 }
424}
425
426void OpenGLState::ApplyRenderBuffer() {
427 if (cur_state.renderbuffer != renderbuffer) {
428 cur_state.renderbuffer = renderbuffer;
429 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
430 }
431}
432
433void OpenGLState::ApplyTextures() {
434 const std::size_t size = std::size(textures);
435 for (std::size_t i = 0; i < size; ++i) {
436 if (UpdateValue(cur_state.textures[i], textures[i])) {
437 // BindTextureUnit doesn't support binding null textures, skip those binds.
438 // TODO(Rodrigo): Stop using null textures
439 if (textures[i] != 0) {
440 glBindTextureUnit(static_cast<GLuint>(i), textures[i]);
441 }
442 }
443 }
444}
445
446void OpenGLState::ApplySamplers() {
447 const std::size_t size = std::size(samplers);
448 for (std::size_t i = 0; i < size; ++i) {
449 if (UpdateValue(cur_state.samplers[i], samplers[i])) {
450 glBindSampler(static_cast<GLuint>(i), samplers[i]);
451 }
452 }
453}
454
455void OpenGLState::ApplyImages() {
456 if (const auto update = UpdateArray(cur_state.images, images)) {
457 glBindImageTextures(update->first, update->second, images.data() + update->first);
458 }
459}
460
461void OpenGLState::Apply() {
462 MICROPROFILE_SCOPE(OpenGL_State);
463 ApplyFramebufferState();
464 ApplyVertexArrayState();
465 ApplyShaderProgram();
466 ApplyProgramPipeline();
467 ApplyClipDistances();
468 ApplyPointSize();
469 ApplyFragmentColorClamp();
470 ApplyMultisample();
471 ApplyRasterizerDiscard();
472 ApplyColorMask();
473 ApplyDepthClamp();
474 ApplyViewport();
475 ApplyStencilTest();
476 ApplySRgb();
477 ApplyCulling();
478 ApplyDepth();
479 ApplyPrimitiveRestart();
480 ApplyBlending();
481 ApplyLogicOp();
482 ApplyTextures();
483 ApplySamplers();
484 ApplyImages();
485 ApplyPolygonOffset();
486 ApplyAlphaTest();
487 ApplyClipControl();
488 ApplyRenderBuffer();
489}
490
491void OpenGLState::EmulateViewportWithScissor() {
492 auto& current = viewports[0];
493 if (current.scissor.enabled) {
494 const GLint left = std::max(current.x, current.scissor.x);
495 const GLint right =
496 std::max(current.x + current.width, current.scissor.x + current.scissor.width);
497 const GLint bottom = std::max(current.y, current.scissor.y);
498 const GLint top =
499 std::max(current.y + current.height, current.scissor.y + current.scissor.height);
500 current.scissor.x = std::max(left, 0);
501 current.scissor.y = std::max(bottom, 0);
502 current.scissor.width = std::max(right - left, 0);
503 current.scissor.height = std::max(top - bottom, 0);
504 } else {
505 current.scissor.enabled = true;
506 current.scissor.x = current.x;
507 current.scissor.y = current.y;
508 current.scissor.width = current.width;
509 current.scissor.height = current.height;
510 }
511}
512
513OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
514 for (auto& texture : textures) {
515 if (texture == handle) {
516 texture = 0;
517 }
518 }
519 return *this;
520}
521
522OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
523 for (auto& sampler : samplers) {
524 if (sampler == handle) {
525 sampler = 0;
526 }
527 }
528 return *this;
529}
530
531OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
532 if (draw.shader_program == handle) {
533 draw.shader_program = 0;
534 }
535 return *this;
536}
537
538OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
539 if (draw.program_pipeline == handle) {
540 draw.program_pipeline = 0;
541 }
542 return *this;
543}
544
545OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
546 if (draw.vertex_array == handle) {
547 draw.vertex_array = 0;
548 }
549 return *this;
550}
551
552OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
553 if (draw.read_framebuffer == handle) {
554 draw.read_framebuffer = 0;
555 }
556 if (draw.draw_framebuffer == handle) {
557 draw.draw_framebuffer = 0;
558 }
559 return *this;
560}
561
562OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) {
563 if (renderbuffer == handle) {
564 renderbuffer = 0;
565 }
566 return *this;
567}
568
569} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
deleted file mode 100644
index bce662f2c..000000000
--- a/src/video_core/renderer_opengl/gl_state.h
+++ /dev/null
@@ -1,251 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <type_traits>
9#include <glad/glad.h>
10#include "video_core/engines/maxwell_3d.h"
11
12namespace OpenGL {
13
14class OpenGLState {
15public:
16 struct {
17 bool enabled = false; // GL_FRAMEBUFFER_SRGB
18 } framebuffer_srgb;
19
20 struct {
21 bool alpha_to_coverage = false; // GL_ALPHA_TO_COVERAGE
22 bool alpha_to_one = false; // GL_ALPHA_TO_ONE
23 } multisample_control;
24
25 struct {
26 bool enabled = false; // GL_CLAMP_FRAGMENT_COLOR_ARB
27 } fragment_color_clamp;
28
29 struct {
30 bool far_plane = false;
31 bool near_plane = false;
32 } depth_clamp; // GL_DEPTH_CLAMP
33
34 struct {
35 bool enabled = false; // GL_CULL_FACE
36 GLenum mode = GL_BACK; // GL_CULL_FACE_MODE
37 GLenum front_face = GL_CCW; // GL_FRONT_FACE
38 } cull;
39
40 struct {
41 bool test_enabled = false; // GL_DEPTH_TEST
42 GLboolean write_mask = GL_TRUE; // GL_DEPTH_WRITEMASK
43 GLenum test_func = GL_LESS; // GL_DEPTH_FUNC
44 } depth;
45
46 struct {
47 bool enabled = false;
48 GLuint index = 0;
49 } primitive_restart; // GL_PRIMITIVE_RESTART
50
51 bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
52
53 struct ColorMask {
54 GLboolean red_enabled = GL_TRUE;
55 GLboolean green_enabled = GL_TRUE;
56 GLboolean blue_enabled = GL_TRUE;
57 GLboolean alpha_enabled = GL_TRUE;
58 };
59 std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
60 color_mask; // GL_COLOR_WRITEMASK
61
62 struct {
63 bool test_enabled = false; // GL_STENCIL_TEST
64 struct {
65 GLenum test_func = GL_ALWAYS; // GL_STENCIL_FUNC
66 GLint test_ref = 0; // GL_STENCIL_REF
67 GLuint test_mask = 0xFFFFFFFF; // GL_STENCIL_VALUE_MASK
68 GLuint write_mask = 0xFFFFFFFF; // GL_STENCIL_WRITEMASK
69 GLenum action_stencil_fail = GL_KEEP; // GL_STENCIL_FAIL
70 GLenum action_depth_fail = GL_KEEP; // GL_STENCIL_PASS_DEPTH_FAIL
71 GLenum action_depth_pass = GL_KEEP; // GL_STENCIL_PASS_DEPTH_PASS
72 } front, back;
73 } stencil;
74
75 struct Blend {
76 bool enabled = false; // GL_BLEND
77 GLenum rgb_equation = GL_FUNC_ADD; // GL_BLEND_EQUATION_RGB
78 GLenum a_equation = GL_FUNC_ADD; // GL_BLEND_EQUATION_ALPHA
79 GLenum src_rgb_func = GL_ONE; // GL_BLEND_SRC_RGB
80 GLenum dst_rgb_func = GL_ZERO; // GL_BLEND_DST_RGB
81 GLenum src_a_func = GL_ONE; // GL_BLEND_SRC_ALPHA
82 GLenum dst_a_func = GL_ZERO; // GL_BLEND_DST_ALPHA
83 };
84 std::array<Blend, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> blend;
85
86 struct {
87 bool enabled = false;
88 } independant_blend;
89
90 struct {
91 GLclampf red = 0.0f;
92 GLclampf green = 0.0f;
93 GLclampf blue = 0.0f;
94 GLclampf alpha = 0.0f;
95 } blend_color; // GL_BLEND_COLOR
96
97 struct {
98 bool enabled = false; // GL_LOGIC_OP_MODE
99 GLenum operation = GL_COPY;
100 } logic_op;
101
102 static constexpr std::size_t NumSamplers = 32 * 5;
103 static constexpr std::size_t NumImages = 8 * 5;
104 std::array<GLuint, NumSamplers> textures = {};
105 std::array<GLuint, NumSamplers> samplers = {};
106 std::array<GLuint, NumImages> images = {};
107
108 struct {
109 GLuint read_framebuffer = 0; // GL_READ_FRAMEBUFFER_BINDING
110 GLuint draw_framebuffer = 0; // GL_DRAW_FRAMEBUFFER_BINDING
111 GLuint vertex_array = 0; // GL_VERTEX_ARRAY_BINDING
112 GLuint shader_program = 0; // GL_CURRENT_PROGRAM
113 GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
114 } draw;
115
116 struct Viewport {
117 GLint x = 0;
118 GLint y = 0;
119 GLint width = 0;
120 GLint height = 0;
121 GLfloat depth_range_near = 0.0f; // GL_DEPTH_RANGE
122 GLfloat depth_range_far = 1.0f; // GL_DEPTH_RANGE
123 struct {
124 bool enabled = false; // GL_SCISSOR_TEST
125 GLint x = 0;
126 GLint y = 0;
127 GLsizei width = 0;
128 GLsizei height = 0;
129 } scissor;
130 };
131 std::array<Viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
132
133 struct {
134 bool program_control = false; // GL_PROGRAM_POINT_SIZE
135 bool sprite = false; // GL_POINT_SPRITE
136 GLfloat size = 1.0f; // GL_POINT_SIZE
137 } point;
138
139 struct {
140 bool point_enable = false;
141 bool line_enable = false;
142 bool fill_enable = false;
143 GLfloat units = 0.0f;
144 GLfloat factor = 0.0f;
145 GLfloat clamp = 0.0f;
146 } polygon_offset;
147
148 struct {
149 bool enabled = false; // GL_ALPHA_TEST
150 GLenum func = GL_ALWAYS; // GL_ALPHA_TEST_FUNC
151 GLfloat ref = 0.0f; // GL_ALPHA_TEST_REF
152 } alpha_test;
153
154 std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
155
156 struct {
157 GLenum origin = GL_LOWER_LEFT;
158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
159 } clip_control;
160
161 GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
162
163 OpenGLState();
164
165 /// Get the currently active OpenGL state
166 static OpenGLState GetCurState() {
167 return cur_state;
168 }
169
170 void SetDefaultViewports();
171 /// Apply this state as the current OpenGL state
172 void Apply();
173
174 void ApplyFramebufferState();
175 void ApplyVertexArrayState();
176 void ApplyShaderProgram();
177 void ApplyProgramPipeline();
178 void ApplyClipDistances();
179 void ApplyPointSize();
180 void ApplyFragmentColorClamp();
181 void ApplyMultisample();
182 void ApplySRgb();
183 void ApplyCulling();
184 void ApplyRasterizerDiscard();
185 void ApplyColorMask();
186 void ApplyDepth();
187 void ApplyPrimitiveRestart();
188 void ApplyStencilTest();
189 void ApplyViewport();
190 void ApplyTargetBlending(std::size_t target, bool force);
191 void ApplyGlobalBlending();
192 void ApplyBlending();
193 void ApplyLogicOp();
194 void ApplyTextures();
195 void ApplySamplers();
196 void ApplyImages();
197 void ApplyDepthClamp();
198 void ApplyPolygonOffset();
199 void ApplyAlphaTest();
200 void ApplyClipControl();
201 void ApplyRenderBuffer();
202
203 /// Resets any references to the given resource
204 OpenGLState& UnbindTexture(GLuint handle);
205 OpenGLState& ResetSampler(GLuint handle);
206 OpenGLState& ResetProgram(GLuint handle);
207 OpenGLState& ResetPipeline(GLuint handle);
208 OpenGLState& ResetVertexArray(GLuint handle);
209 OpenGLState& ResetFramebuffer(GLuint handle);
210 OpenGLState& ResetRenderbuffer(GLuint handle);
211
212 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
213 void EmulateViewportWithScissor();
214
215 void MarkDirtyBlendState() {
216 dirty.blend_state = true;
217 }
218
219 void MarkDirtyStencilState() {
220 dirty.stencil_state = true;
221 }
222
223 void MarkDirtyPolygonOffset() {
224 dirty.polygon_offset = true;
225 }
226
227 void MarkDirtyColorMask() {
228 dirty.color_mask = true;
229 }
230
231 void AllDirty() {
232 dirty.blend_state = true;
233 dirty.stencil_state = true;
234 dirty.polygon_offset = true;
235 dirty.color_mask = true;
236 }
237
238private:
239 static OpenGLState cur_state;
240
241 struct {
242 bool blend_state;
243 bool stencil_state;
244 bool viewport_state;
245 bool polygon_offset;
246 bool color_mask;
247 } dirty{};
248};
249static_assert(std::is_trivially_copyable_v<OpenGLState>);
250
251} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
new file mode 100644
index 000000000..1e43c9ec0
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -0,0 +1,238 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <cstddef>
8
9#include "common/common_types.h"
10#include "core/core.h"
11#include "video_core/engines/maxwell_3d.h"
12#include "video_core/gpu.h"
13#include "video_core/renderer_opengl/gl_state_tracker.h"
14
15#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
16#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / sizeof(u32))
17
18namespace OpenGL {
19
20namespace {
21
22using namespace Dirty;
23using namespace VideoCommon::Dirty;
24using Tegra::Engines::Maxwell3D;
25using Regs = Maxwell3D::Regs;
26using Tables = Maxwell3D::DirtyState::Tables;
27using Table = Maxwell3D::DirtyState::Table;
28
29void SetupDirtyColorMasks(Tables& tables) {
30 tables[0][OFF(color_mask_common)] = ColorMaskCommon;
31 for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
32 const std::size_t offset = OFF(color_mask) + rt * NUM(color_mask[0]);
33 FillBlock(tables[0], offset, NUM(color_mask[0]), ColorMask0 + rt);
34 }
35
36 FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMasks);
37}
38
39void SetupDirtyVertexArrays(Tables& tables) {
40 static constexpr std::size_t num_array = 3;
41 static constexpr std::size_t instance_base_offset = 3;
42 for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
43 const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]);
44 const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]);
45
46 FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers);
47 FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers);
48
49 const std::size_t instance_array_offset = array_offset + instance_base_offset;
50 tables[0][instance_array_offset] = static_cast<u8>(VertexInstance0 + i);
51 tables[1][instance_array_offset] = VertexInstances;
52
53 const std::size_t instance_offset = OFF(instanced_arrays) + i;
54 tables[0][instance_offset] = static_cast<u8>(VertexInstance0 + i);
55 tables[1][instance_offset] = VertexInstances;
56 }
57}
58
59void SetupDirtyVertexFormat(Tables& tables) {
60 for (std::size_t i = 0; i < Regs::NumVertexAttributes; ++i) {
61 const std::size_t offset = OFF(vertex_attrib_format) + i * NUM(vertex_attrib_format[0]);
62 FillBlock(tables[0], offset, NUM(vertex_attrib_format[0]), VertexFormat0 + i);
63 }
64
65 FillBlock(tables[1], OFF(vertex_attrib_format), Regs::NumVertexAttributes, VertexFormats);
66}
67
68void SetupDirtyViewports(Tables& tables) {
69 for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
70 const std::size_t transf_offset = OFF(viewport_transform) + i * NUM(viewport_transform[0]);
71 const std::size_t viewport_offset = OFF(viewports) + i * NUM(viewports[0]);
72
73 FillBlock(tables[0], transf_offset, NUM(viewport_transform[0]), Viewport0 + i);
74 FillBlock(tables[0], viewport_offset, NUM(viewports[0]), Viewport0 + i);
75 }
76
77 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
78 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
79
80 tables[0][OFF(viewport_transform_enabled)] = ViewportTransform;
81 tables[1][OFF(viewport_transform_enabled)] = Viewports;
82}
83
84void SetupDirtyScissors(Tables& tables) {
85 for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
86 const std::size_t offset = OFF(scissor_test) + i * NUM(scissor_test[0]);
87 FillBlock(tables[0], offset, NUM(scissor_test[0]), Scissor0 + i);
88 }
89 FillBlock(tables[1], OFF(scissor_test), NUM(scissor_test), Scissors);
90}
91
92void SetupDirtyShaders(Tables& tables) {
93 FillBlock(tables[0], OFF(shader_config[0]), NUM(shader_config[0]) * Regs::MaxShaderProgram,
94 Shaders);
95}
96
97void SetupDirtyDepthTest(Tables& tables) {
98 auto& table = tables[0];
99 table[OFF(depth_test_enable)] = DepthTest;
100 table[OFF(depth_write_enabled)] = DepthMask;
101 table[OFF(depth_test_func)] = DepthTest;
102}
103
104void SetupDirtyStencilTest(Tables& tables) {
105 static constexpr std::array offsets = {
106 OFF(stencil_enable), OFF(stencil_front_func_func), OFF(stencil_front_func_ref),
107 OFF(stencil_front_func_mask), OFF(stencil_front_op_fail), OFF(stencil_front_op_zfail),
108 OFF(stencil_front_op_zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable),
109 OFF(stencil_back_func_func), OFF(stencil_back_func_ref), OFF(stencil_back_func_mask),
110 OFF(stencil_back_op_fail), OFF(stencil_back_op_zfail), OFF(stencil_back_op_zpass),
111 OFF(stencil_back_mask)};
112 for (const auto offset : offsets) {
113 tables[0][offset] = StencilTest;
114 }
115}
116
117void SetupDirtyAlphaTest(Tables& tables) {
118 auto& table = tables[0];
119 table[OFF(alpha_test_ref)] = AlphaTest;
120 table[OFF(alpha_test_func)] = AlphaTest;
121 table[OFF(alpha_test_enabled)] = AlphaTest;
122}
123
124void SetupDirtyBlend(Tables& tables) {
125 FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
126
127 tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled;
128
129 for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
130 const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]);
131 FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i);
132
133 tables[0][OFF(blend.enable) + i] = static_cast<u8>(BlendState0 + i);
134 }
135 FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates);
136 FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates);
137}
138
139void SetupDirtyPrimitiveRestart(Tables& tables) {
140 FillBlock(tables[0], OFF(primitive_restart), NUM(primitive_restart), PrimitiveRestart);
141}
142
143void SetupDirtyPolygonOffset(Tables& tables) {
144 auto& table = tables[0];
145 table[OFF(polygon_offset_fill_enable)] = PolygonOffset;
146 table[OFF(polygon_offset_line_enable)] = PolygonOffset;
147 table[OFF(polygon_offset_point_enable)] = PolygonOffset;
148 table[OFF(polygon_offset_factor)] = PolygonOffset;
149 table[OFF(polygon_offset_units)] = PolygonOffset;
150 table[OFF(polygon_offset_clamp)] = PolygonOffset;
151}
152
153void SetupDirtyMultisampleControl(Tables& tables) {
154 FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl);
155}
156
157void SetupDirtyRasterizeEnable(Tables& tables) {
158 tables[0][OFF(rasterize_enable)] = RasterizeEnable;
159}
160
161void SetupDirtyFramebufferSRGB(Tables& tables) {
162 tables[0][OFF(framebuffer_srgb)] = FramebufferSRGB;
163}
164
165void SetupDirtyLogicOp(Tables& tables) {
166 FillBlock(tables[0], OFF(logic_op), NUM(logic_op), LogicOp);
167}
168
169void SetupDirtyFragmentClampColor(Tables& tables) {
170 tables[0][OFF(frag_color_clamp)] = FragmentClampColor;
171}
172
173void SetupDirtyPointSize(Tables& tables) {
174 tables[0][OFF(vp_point_size)] = PointSize;
175 tables[0][OFF(point_size)] = PointSize;
176 tables[0][OFF(point_sprite_enable)] = PointSize;
177}
178
179void SetupDirtyClipControl(Tables& tables) {
180 auto& table = tables[0];
181 table[OFF(screen_y_control)] = ClipControl;
182 table[OFF(depth_mode)] = ClipControl;
183}
184
185void SetupDirtyDepthClampEnabled(Tables& tables) {
186 tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled;
187}
188
189void SetupDirtyMisc(Tables& tables) {
190 auto& table = tables[0];
191
192 table[OFF(clip_distance_enabled)] = ClipDistances;
193
194 table[OFF(front_face)] = FrontFace;
195
196 table[OFF(cull_test_enabled)] = CullTest;
197 table[OFF(cull_face)] = CullTest;
198}
199
200} // Anonymous namespace
201
202StateTracker::StateTracker(Core::System& system) : system{system} {}
203
204void StateTracker::Initialize() {
205 auto& dirty = system.GPU().Maxwell3D().dirty;
206 auto& tables = dirty.tables;
207 SetupDirtyRenderTargets(tables);
208 SetupDirtyColorMasks(tables);
209 SetupDirtyViewports(tables);
210 SetupDirtyScissors(tables);
211 SetupDirtyVertexArrays(tables);
212 SetupDirtyVertexFormat(tables);
213 SetupDirtyShaders(tables);
214 SetupDirtyDepthTest(tables);
215 SetupDirtyStencilTest(tables);
216 SetupDirtyAlphaTest(tables);
217 SetupDirtyBlend(tables);
218 SetupDirtyPrimitiveRestart(tables);
219 SetupDirtyPolygonOffset(tables);
220 SetupDirtyMultisampleControl(tables);
221 SetupDirtyRasterizeEnable(tables);
222 SetupDirtyFramebufferSRGB(tables);
223 SetupDirtyLogicOp(tables);
224 SetupDirtyFragmentClampColor(tables);
225 SetupDirtyPointSize(tables);
226 SetupDirtyClipControl(tables);
227 SetupDirtyDepthClampEnabled(tables);
228 SetupDirtyMisc(tables);
229
230 auto& store = dirty.on_write_stores;
231 SetupCommonOnWriteStores(store);
232 store[VertexBuffers] = true;
233 for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
234 store[VertexBuffer0 + i] = true;
235 }
236}
237
238} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
new file mode 100644
index 000000000..e08482911
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -0,0 +1,204 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <limits>
8
9#include <glad/glad.h>
10
11#include "common/common_types.h"
12#include "core/core.h"
13#include "video_core/dirty_flags.h"
14#include "video_core/engines/maxwell_3d.h"
15
16namespace Core {
17class System;
18}
19
20namespace OpenGL {
21
22namespace Dirty {
23
24enum : u8 {
25 First = VideoCommon::Dirty::LastCommonEntry,
26
27 VertexFormats,
28 VertexFormat0,
29 VertexFormat31 = VertexFormat0 + 31,
30
31 VertexBuffers,
32 VertexBuffer0,
33 VertexBuffer31 = VertexBuffer0 + 31,
34
35 VertexInstances,
36 VertexInstance0,
37 VertexInstance31 = VertexInstance0 + 31,
38
39 ViewportTransform,
40 Viewports,
41 Viewport0,
42 Viewport15 = Viewport0 + 15,
43
44 Scissors,
45 Scissor0,
46 Scissor15 = Scissor0 + 15,
47
48 ColorMaskCommon,
49 ColorMasks,
50 ColorMask0,
51 ColorMask7 = ColorMask0 + 7,
52
53 BlendColor,
54 BlendIndependentEnabled,
55 BlendStates,
56 BlendState0,
57 BlendState7 = BlendState0 + 7,
58
59 Shaders,
60 ClipDistances,
61
62 ColorMask,
63 FrontFace,
64 CullTest,
65 DepthMask,
66 DepthTest,
67 StencilTest,
68 AlphaTest,
69 PrimitiveRestart,
70 PolygonOffset,
71 MultisampleControl,
72 RasterizeEnable,
73 FramebufferSRGB,
74 LogicOp,
75 FragmentClampColor,
76 PointSize,
77 ClipControl,
78 DepthClampEnabled,
79
80 Last
81};
82static_assert(Last <= std::numeric_limits<u8>::max());
83
84} // namespace Dirty
85
86class StateTracker {
87public:
88 explicit StateTracker(Core::System& system);
89
90 void Initialize();
91
92 void BindIndexBuffer(GLuint new_index_buffer) {
93 if (index_buffer == new_index_buffer) {
94 return;
95 }
96 index_buffer = new_index_buffer;
97 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, new_index_buffer);
98 }
99
100 void NotifyScreenDrawVertexArray() {
101 auto& flags = system.GPU().Maxwell3D().dirty.flags;
102 flags[OpenGL::Dirty::VertexFormats] = true;
103 flags[OpenGL::Dirty::VertexFormat0 + 0] = true;
104 flags[OpenGL::Dirty::VertexFormat0 + 1] = true;
105
106 flags[OpenGL::Dirty::VertexBuffers] = true;
107 flags[OpenGL::Dirty::VertexBuffer0] = true;
108
109 flags[OpenGL::Dirty::VertexInstances] = true;
110 flags[OpenGL::Dirty::VertexInstance0 + 0] = true;
111 flags[OpenGL::Dirty::VertexInstance0 + 1] = true;
112 }
113
114 void NotifyViewport0() {
115 auto& flags = system.GPU().Maxwell3D().dirty.flags;
116 flags[OpenGL::Dirty::Viewports] = true;
117 flags[OpenGL::Dirty::Viewport0] = true;
118 }
119
120 void NotifyScissor0() {
121 auto& flags = system.GPU().Maxwell3D().dirty.flags;
122 flags[OpenGL::Dirty::Scissors] = true;
123 flags[OpenGL::Dirty::Scissor0] = true;
124 }
125
126 void NotifyColorMask0() {
127 auto& flags = system.GPU().Maxwell3D().dirty.flags;
128 flags[OpenGL::Dirty::ColorMasks] = true;
129 flags[OpenGL::Dirty::ColorMask0] = true;
130 }
131
132 void NotifyBlend0() {
133 auto& flags = system.GPU().Maxwell3D().dirty.flags;
134 flags[OpenGL::Dirty::BlendStates] = true;
135 flags[OpenGL::Dirty::BlendState0] = true;
136 }
137
138 void NotifyFramebuffer() {
139 auto& flags = system.GPU().Maxwell3D().dirty.flags;
140 flags[VideoCommon::Dirty::RenderTargets] = true;
141 }
142
143 void NotifyFrontFace() {
144 auto& flags = system.GPU().Maxwell3D().dirty.flags;
145 flags[OpenGL::Dirty::FrontFace] = true;
146 }
147
148 void NotifyCullTest() {
149 auto& flags = system.GPU().Maxwell3D().dirty.flags;
150 flags[OpenGL::Dirty::CullTest] = true;
151 }
152
153 void NotifyDepthMask() {
154 auto& flags = system.GPU().Maxwell3D().dirty.flags;
155 flags[OpenGL::Dirty::DepthMask] = true;
156 }
157
158 void NotifyDepthTest() {
159 auto& flags = system.GPU().Maxwell3D().dirty.flags;
160 flags[OpenGL::Dirty::DepthTest] = true;
161 }
162
163 void NotifyStencilTest() {
164 auto& flags = system.GPU().Maxwell3D().dirty.flags;
165 flags[OpenGL::Dirty::StencilTest] = true;
166 }
167
168 void NotifyPolygonOffset() {
169 auto& flags = system.GPU().Maxwell3D().dirty.flags;
170 flags[OpenGL::Dirty::PolygonOffset] = true;
171 }
172
173 void NotifyRasterizeEnable() {
174 auto& flags = system.GPU().Maxwell3D().dirty.flags;
175 flags[OpenGL::Dirty::RasterizeEnable] = true;
176 }
177
178 void NotifyFramebufferSRGB() {
179 auto& flags = system.GPU().Maxwell3D().dirty.flags;
180 flags[OpenGL::Dirty::FramebufferSRGB] = true;
181 }
182
183 void NotifyLogicOp() {
184 auto& flags = system.GPU().Maxwell3D().dirty.flags;
185 flags[OpenGL::Dirty::LogicOp] = true;
186 }
187
188 void NotifyClipControl() {
189 auto& flags = system.GPU().Maxwell3D().dirty.flags;
190 flags[OpenGL::Dirty::ClipControl] = true;
191 }
192
193 void NotifyAlphaTest() {
194 auto& flags = system.GPU().Maxwell3D().dirty.flags;
195 flags[OpenGL::Dirty::AlphaTest] = true;
196 }
197
198private:
199 Core::System& system;
200
201 GLuint index_buffer = 0;
202};
203
204} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index 35ba334e4..6ec328c53 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -7,7 +7,6 @@
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/microprofile.h" 9#include "common/microprofile.h"
10#include "video_core/renderer_opengl/gl_state.h"
11#include "video_core/renderer_opengl/gl_stream_buffer.h" 10#include "video_core/renderer_opengl/gl_stream_buffer.h"
12 11
13MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", 12MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index cf934b0d8..2d3838a7a 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -10,7 +10,7 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "video_core/morton.h" 11#include "video_core/morton.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 12#include "video_core/renderer_opengl/gl_resource_manager.h"
13#include "video_core/renderer_opengl/gl_state.h" 13#include "video_core/renderer_opengl/gl_state_tracker.h"
14#include "video_core/renderer_opengl/gl_texture_cache.h" 14#include "video_core/renderer_opengl/gl_texture_cache.h"
15#include "video_core/renderer_opengl/utils.h" 15#include "video_core/renderer_opengl/utils.h"
16#include "video_core/texture_cache/surface_base.h" 16#include "video_core/texture_cache/surface_base.h"
@@ -397,6 +397,7 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
397 const bool is_proxy) 397 const bool is_proxy)
398 : VideoCommon::ViewBase(params), surface{surface}, is_proxy{is_proxy} { 398 : VideoCommon::ViewBase(params), surface{surface}, is_proxy{is_proxy} {
399 target = GetTextureTarget(params.target); 399 target = GetTextureTarget(params.target);
400 format = GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format;
400 if (!is_proxy) { 401 if (!is_proxy) {
401 texture_view = CreateTextureView(); 402 texture_view = CreateTextureView();
402 } 403 }
@@ -467,25 +468,20 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou
467} 468}
468 469
469OGLTextureView CachedSurfaceView::CreateTextureView() const { 470OGLTextureView CachedSurfaceView::CreateTextureView() const {
470 const auto& owner_params = surface.GetSurfaceParams();
471 OGLTextureView texture_view; 471 OGLTextureView texture_view;
472 texture_view.Create(); 472 texture_view.Create();
473 473
474 const GLuint handle{texture_view.handle}; 474 glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level,
475 const FormatTuple& tuple{GetFormatTuple(owner_params.pixel_format)};
476
477 glTextureView(handle, target, surface.texture.handle, tuple.internal_format, params.base_level,
478 params.num_levels, params.base_layer, params.num_layers); 475 params.num_levels, params.base_layer, params.num_layers);
479 476 ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle);
480 ApplyTextureDefaults(owner_params, handle);
481 477
482 return texture_view; 478 return texture_view;
483} 479}
484 480
485TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, 481TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
486 VideoCore::RasterizerInterface& rasterizer, 482 VideoCore::RasterizerInterface& rasterizer,
487 const Device& device) 483 const Device& device, StateTracker& state_tracker)
488 : TextureCacheBase{system, rasterizer} { 484 : TextureCacheBase{system, rasterizer}, state_tracker{state_tracker} {
489 src_framebuffer.Create(); 485 src_framebuffer.Create();
490 dst_framebuffer.Create(); 486 dst_framebuffer.Create();
491} 487}
@@ -519,25 +515,26 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
519 const Tegra::Engines::Fermi2D::Config& copy_config) { 515 const Tegra::Engines::Fermi2D::Config& copy_config) {
520 const auto& src_params{src_view->GetSurfaceParams()}; 516 const auto& src_params{src_view->GetSurfaceParams()};
521 const auto& dst_params{dst_view->GetSurfaceParams()}; 517 const auto& dst_params{dst_view->GetSurfaceParams()};
518 UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D);
519 UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
522 520
523 OpenGLState prev_state{OpenGLState::GetCurState()}; 521 state_tracker.NotifyScissor0();
524 SCOPE_EXIT({ 522 state_tracker.NotifyFramebuffer();
525 prev_state.AllDirty(); 523 state_tracker.NotifyRasterizeEnable();
526 prev_state.Apply(); 524 state_tracker.NotifyFramebufferSRGB();
527 });
528
529 OpenGLState state;
530 state.draw.read_framebuffer = src_framebuffer.handle;
531 state.draw.draw_framebuffer = dst_framebuffer.handle;
532 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
533 state.AllDirty();
534 state.Apply();
535 525
536 u32 buffers{}; 526 if (dst_params.srgb_conversion) {
527 glEnable(GL_FRAMEBUFFER_SRGB);
528 } else {
529 glDisable(GL_FRAMEBUFFER_SRGB);
530 }
531 glDisable(GL_RASTERIZER_DISCARD);
532 glDisablei(GL_SCISSOR_TEST, 0);
537 533
538 UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D); 534 glBindFramebuffer(GL_READ_FRAMEBUFFER, src_framebuffer.handle);
539 UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D); 535 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer.handle);
540 536
537 GLenum buffers = 0;
541 if (src_params.type == SurfaceType::ColorTexture) { 538 if (src_params.type == SurfaceType::ColorTexture) {
542 src_view->Attach(GL_COLOR_ATTACHMENT0, GL_READ_FRAMEBUFFER); 539 src_view->Attach(GL_COLOR_ATTACHMENT0, GL_READ_FRAMEBUFFER);
543 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 540 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 8e13ab38b..6658c6ffd 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -27,6 +27,7 @@ using VideoCommon::ViewParams;
27class CachedSurfaceView; 27class CachedSurfaceView;
28class CachedSurface; 28class CachedSurface;
29class TextureCacheOpenGL; 29class TextureCacheOpenGL;
30class StateTracker;
30 31
31using Surface = std::shared_ptr<CachedSurface>; 32using Surface = std::shared_ptr<CachedSurface>;
32using View = std::shared_ptr<CachedSurfaceView>; 33using View = std::shared_ptr<CachedSurfaceView>;
@@ -96,6 +97,10 @@ public:
96 return texture_view.handle; 97 return texture_view.handle;
97 } 98 }
98 99
100 GLenum GetFormat() const {
101 return format;
102 }
103
99 const SurfaceParams& GetSurfaceParams() const { 104 const SurfaceParams& GetSurfaceParams() const {
100 return surface.GetSurfaceParams(); 105 return surface.GetSurfaceParams();
101 } 106 }
@@ -113,6 +118,7 @@ private:
113 118
114 CachedSurface& surface; 119 CachedSurface& surface;
115 GLenum target{}; 120 GLenum target{};
121 GLenum format{};
116 122
117 OGLTextureView texture_view; 123 OGLTextureView texture_view;
118 u32 swizzle{}; 124 u32 swizzle{};
@@ -122,7 +128,7 @@ private:
122class TextureCacheOpenGL final : public TextureCacheBase { 128class TextureCacheOpenGL final : public TextureCacheBase {
123public: 129public:
124 explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 130 explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
125 const Device& device); 131 const Device& device, StateTracker& state_tracker);
126 ~TextureCacheOpenGL(); 132 ~TextureCacheOpenGL();
127 133
128protected: 134protected:
@@ -139,6 +145,8 @@ protected:
139private: 145private:
140 GLuint FetchPBO(std::size_t buffer_size); 146 GLuint FetchPBO(std::size_t buffer_size);
141 147
148 StateTracker& state_tracker;
149
142 OGLFramebuffer src_framebuffer; 150 OGLFramebuffer src_framebuffer;
143 OGLFramebuffer dst_framebuffer; 151 OGLFramebuffer dst_framebuffer;
144 std::unordered_map<u32, OGLBuffer> copy_pbo_cache; 152 std::unordered_map<u32, OGLBuffer> copy_pbo_cache;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index d3dea3659..494e38e7a 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -425,24 +425,24 @@ inline GLenum StencilOp(Maxwell::StencilOp stencil) {
425 return GL_KEEP; 425 return GL_KEEP;
426} 426}
427 427
428inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { 428inline GLenum FrontFace(Maxwell::FrontFace front_face) {
429 switch (front_face) { 429 switch (front_face) {
430 case Maxwell::Cull::FrontFace::ClockWise: 430 case Maxwell::FrontFace::ClockWise:
431 return GL_CW; 431 return GL_CW;
432 case Maxwell::Cull::FrontFace::CounterClockWise: 432 case Maxwell::FrontFace::CounterClockWise:
433 return GL_CCW; 433 return GL_CCW;
434 } 434 }
435 LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); 435 LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
436 return GL_CCW; 436 return GL_CCW;
437} 437}
438 438
439inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { 439inline GLenum CullFace(Maxwell::CullFace cull_face) {
440 switch (cull_face) { 440 switch (cull_face) {
441 case Maxwell::Cull::CullFace::Front: 441 case Maxwell::CullFace::Front:
442 return GL_FRONT; 442 return GL_FRONT;
443 case Maxwell::Cull::CullFace::Back: 443 case Maxwell::CullFace::Back:
444 return GL_BACK; 444 return GL_BACK;
445 case Maxwell::Cull::CullFace::FrontAndBack: 445 case Maxwell::CullFace::FrontAndBack:
446 return GL_FRONT_AND_BACK; 446 return GL_FRONT_AND_BACK;
447 } 447 }
448 LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); 448 LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a4340b502..c05677cd9 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -20,6 +20,7 @@
20#include "core/telemetry_session.h" 20#include "core/telemetry_session.h"
21#include "video_core/morton.h" 21#include "video_core/morton.h"
22#include "video_core/renderer_opengl/gl_rasterizer.h" 22#include "video_core/renderer_opengl/gl_rasterizer.h"
23#include "video_core/renderer_opengl/gl_shader_manager.h"
23#include "video_core/renderer_opengl/renderer_opengl.h" 24#include "video_core/renderer_opengl/renderer_opengl.h"
24 25
25namespace OpenGL { 26namespace OpenGL {
@@ -86,28 +87,22 @@ public:
86 } 87 }
87 88
88 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) { 89 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
89 OpenGLState prev_state = OpenGLState::GetCurState();
90 OpenGLState state = OpenGLState::GetCurState();
91
92 // Recreate the color texture attachment 90 // Recreate the color texture attachment
93 frame->color.Release(); 91 frame->color.Release();
94 frame->color.Create(); 92 frame->color.Create();
95 state.renderbuffer = frame->color.handle; 93 const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
96 state.Apply(); 94 glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
97 glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
98 95
99 // Recreate the FBO for the render target 96 // Recreate the FBO for the render target
100 frame->render.Release(); 97 frame->render.Release();
101 frame->render.Create(); 98 frame->render.Create();
102 state.draw.read_framebuffer = frame->render.handle; 99 glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
103 state.draw.draw_framebuffer = frame->render.handle;
104 state.Apply();
105 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 100 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
106 frame->color.handle); 101 frame->color.handle);
107 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 102 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
108 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); 103 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
109 } 104 }
110 prev_state.Apply(); 105
111 frame->width = width; 106 frame->width = width;
112 frame->height = height; 107 frame->height = height;
113 frame->color_reloaded = true; 108 frame->color_reloaded = true;
@@ -164,9 +159,13 @@ public:
164 159
165namespace { 160namespace {
166 161
167constexpr char vertex_shader[] = R"( 162constexpr char VERTEX_SHADER[] = R"(
168#version 430 core 163#version 430 core
169 164
165out gl_PerVertex {
166 vec4 gl_Position;
167};
168
170layout (location = 0) in vec2 vert_position; 169layout (location = 0) in vec2 vert_position;
171layout (location = 1) in vec2 vert_tex_coord; 170layout (location = 1) in vec2 vert_tex_coord;
172layout (location = 0) out vec2 frag_tex_coord; 171layout (location = 0) out vec2 frag_tex_coord;
@@ -187,7 +186,7 @@ void main() {
187} 186}
188)"; 187)";
189 188
190constexpr char fragment_shader[] = R"( 189constexpr char FRAGMENT_SHADER[] = R"(
191#version 430 core 190#version 430 core
192 191
193layout (location = 0) in vec2 frag_tex_coord; 192layout (location = 0) in vec2 frag_tex_coord;
@@ -196,7 +195,7 @@ layout (location = 0) out vec4 color;
196layout (binding = 0) uniform sampler2D color_texture; 195layout (binding = 0) uniform sampler2D color_texture;
197 196
198void main() { 197void main() {
199 color = texture(color_texture, frag_tex_coord); 198 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
200} 199}
201)"; 200)";
202 201
@@ -205,8 +204,8 @@ constexpr GLint TexCoordLocation = 1;
205constexpr GLint ModelViewMatrixLocation = 0; 204constexpr GLint ModelViewMatrixLocation = 0;
206 205
207struct ScreenRectVertex { 206struct ScreenRectVertex {
208 constexpr ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v) 207 constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
209 : position{{x, y}}, tex_coord{{u, v}} {} 208 : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
210 209
211 std::array<GLfloat, 2> position; 210 std::array<GLfloat, 2> position;
212 std::array<GLfloat, 2> tex_coord; 211 std::array<GLfloat, 2> tex_coord;
@@ -311,11 +310,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
311 return; 310 return;
312 } 311 }
313 312
314 // Maintain the rasterizer's state as a priority
315 OpenGLState prev_state = OpenGLState::GetCurState();
316 state.AllDirty();
317 state.Apply();
318
319 PrepareRendertarget(framebuffer); 313 PrepareRendertarget(framebuffer);
320 RenderScreenshot(); 314 RenderScreenshot();
321 315
@@ -358,8 +352,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
358 frame->is_srgb = screen_info.display_srgb; 352 frame->is_srgb = screen_info.display_srgb;
359 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height); 353 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
360 } 354 }
361 state.draw.draw_framebuffer = frame->render.handle; 355 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
362 state.Apply();
363 DrawScreen(layout); 356 DrawScreen(layout);
364 // Create a fence for the frontend to wait on and swap this frame to OffTex 357 // Create a fence for the frontend to wait on and swap this frame to OffTex
365 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); 358 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
@@ -368,10 +361,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
368 m_current_frame++; 361 m_current_frame++;
369 rasterizer->TickFrame(); 362 rasterizer->TickFrame();
370 } 363 }
371
372 // Restore the rasterizer state
373 prev_state.AllDirty();
374 prev_state.Apply();
375} 364}
376 365
377void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { 366void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
@@ -442,31 +431,24 @@ void RendererOpenGL::InitOpenGLObjects() {
442 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 431 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
443 0.0f); 432 0.0f);
444 433
445 // Link shaders and get variable locations 434 // Create shader programs
446 shader.CreateFromSource(vertex_shader, nullptr, fragment_shader); 435 OGLShader vertex_shader;
447 state.draw.shader_program = shader.handle; 436 vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER);
448 state.AllDirty(); 437
449 state.Apply(); 438 OGLShader fragment_shader;
439 fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
440
441 vertex_program.Create(true, false, vertex_shader.handle);
442 fragment_program.Create(true, false, fragment_shader.handle);
443
444 // Create program pipeline
445 program_manager.Create();
450 446
451 // Generate VBO handle for drawing 447 // Generate VBO handle for drawing
452 vertex_buffer.Create(); 448 vertex_buffer.Create();
453 449
454 // Generate VAO
455 vertex_array.Create();
456 state.draw.vertex_array = vertex_array.handle;
457
458 // Attach vertex data to VAO 450 // Attach vertex data to VAO
459 glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); 451 glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
460 glVertexArrayAttribFormat(vertex_array.handle, PositionLocation, 2, GL_FLOAT, GL_FALSE,
461 offsetof(ScreenRectVertex, position));
462 glVertexArrayAttribFormat(vertex_array.handle, TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
463 offsetof(ScreenRectVertex, tex_coord));
464 glVertexArrayAttribBinding(vertex_array.handle, PositionLocation, 0);
465 glVertexArrayAttribBinding(vertex_array.handle, TexCoordLocation, 0);
466 glEnableVertexArrayAttrib(vertex_array.handle, PositionLocation);
467 glEnableVertexArrayAttrib(vertex_array.handle, TexCoordLocation);
468 glVertexArrayVertexBuffer(vertex_array.handle, 0, vertex_buffer.handle, 0,
469 sizeof(ScreenRectVertex));
470 452
471 // Allocate textures for the screen 453 // Allocate textures for the screen
472 screen_info.texture.resource.Create(GL_TEXTURE_2D); 454 screen_info.texture.resource.Create(GL_TEXTURE_2D);
@@ -499,7 +481,8 @@ void RendererOpenGL::CreateRasterizer() {
499 if (rasterizer) { 481 if (rasterizer) {
500 return; 482 return;
501 } 483 }
502 rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info); 484 rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info,
485 program_manager, state_tracker);
503} 486}
504 487
505void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 488void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -538,8 +521,19 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
538 glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); 521 glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height);
539} 522}
540 523
541void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, 524void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
542 float h) { 525 if (renderer_settings.set_background_color) {
526 // Update background color before drawing
527 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
528 0.0f);
529 }
530
531 // Set projection matrix
532 const std::array ortho_matrix =
533 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
534 glProgramUniformMatrix3x2fv(vertex_program.handle, ModelViewMatrixLocation, 1, GL_FALSE,
535 std::data(ortho_matrix));
536
543 const auto& texcoords = screen_info.display_texcoords; 537 const auto& texcoords = screen_info.display_texcoords;
544 auto left = texcoords.left; 538 auto left = texcoords.left;
545 auto right = texcoords.right; 539 auto right = texcoords.right;
@@ -571,46 +565,77 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
571 static_cast<f32>(screen_info.texture.height); 565 static_cast<f32>(screen_info.texture.height);
572 } 566 }
573 567
568 const auto& screen = layout.screen;
574 const std::array vertices = { 569 const std::array vertices = {
575 ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v), 570 ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, left * scale_v),
576 ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v), 571 ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, left * scale_v),
577 ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v), 572 ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, right * scale_v),
578 ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v), 573 ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, right * scale_v),
579 }; 574 };
580
581 state.textures[0] = screen_info.display_texture;
582 state.framebuffer_srgb.enabled = screen_info.display_srgb;
583 state.AllDirty();
584 state.Apply();
585 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); 575 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
586 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
587 // Restore default state
588 state.framebuffer_srgb.enabled = false;
589 state.textures[0] = 0;
590 state.AllDirty();
591 state.Apply();
592}
593 576
594void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { 577 // TODO: Signal state tracker about these changes
595 if (renderer_settings.set_background_color) { 578 state_tracker.NotifyScreenDrawVertexArray();
596 // Update background color before drawing 579 state_tracker.NotifyViewport0();
597 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 580 state_tracker.NotifyScissor0();
598 0.0f); 581 state_tracker.NotifyColorMask0();
582 state_tracker.NotifyBlend0();
583 state_tracker.NotifyFramebuffer();
584 state_tracker.NotifyFrontFace();
585 state_tracker.NotifyCullTest();
586 state_tracker.NotifyDepthTest();
587 state_tracker.NotifyStencilTest();
588 state_tracker.NotifyPolygonOffset();
589 state_tracker.NotifyRasterizeEnable();
590 state_tracker.NotifyFramebufferSRGB();
591 state_tracker.NotifyLogicOp();
592 state_tracker.NotifyClipControl();
593 state_tracker.NotifyAlphaTest();
594
595 program_manager.UseVertexShader(vertex_program.handle);
596 program_manager.UseGeometryShader(0);
597 program_manager.UseFragmentShader(fragment_program.handle);
598 program_manager.BindGraphicsPipeline();
599
600 glEnable(GL_CULL_FACE);
601 if (screen_info.display_srgb) {
602 glEnable(GL_FRAMEBUFFER_SRGB);
603 } else {
604 glDisable(GL_FRAMEBUFFER_SRGB);
599 } 605 }
606 glDisable(GL_COLOR_LOGIC_OP);
607 glDisable(GL_DEPTH_TEST);
608 glDisable(GL_STENCIL_TEST);
609 glDisable(GL_POLYGON_OFFSET_FILL);
610 glDisable(GL_RASTERIZER_DISCARD);
611 glDisable(GL_ALPHA_TEST);
612 glDisablei(GL_BLEND, 0);
613 glDisablei(GL_SCISSOR_TEST, 0);
614 glCullFace(GL_BACK);
615 glFrontFace(GL_CW);
616 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
617 glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
618 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
619 static_cast<GLfloat>(layout.height));
620 glDepthRangeIndexed(0, 0.0, 0.0);
621
622 glEnableVertexAttribArray(PositionLocation);
623 glEnableVertexAttribArray(TexCoordLocation);
624 glVertexAttribDivisor(PositionLocation, 0);
625 glVertexAttribDivisor(TexCoordLocation, 0);
626 glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
627 offsetof(ScreenRectVertex, position));
628 glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
629 offsetof(ScreenRectVertex, tex_coord));
630 glVertexAttribBinding(PositionLocation, 0);
631 glVertexAttribBinding(TexCoordLocation, 0);
632 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
633
634 glBindTextureUnit(0, screen_info.display_texture);
635 glBindSampler(0, 0);
600 636
601 const auto& screen = layout.screen;
602
603 glViewport(0, 0, layout.width, layout.height);
604 glClear(GL_COLOR_BUFFER_BIT); 637 glClear(GL_COLOR_BUFFER_BIT);
605 638 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
606 // Set projection matrix
607 const std::array ortho_matrix =
608 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
609 glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
610
611 DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
612 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
613 static_cast<float>(screen.GetHeight()));
614} 639}
615 640
616void RendererOpenGL::TryPresent(int timeout_ms) { 641void RendererOpenGL::TryPresent(int timeout_ms) {
@@ -653,13 +678,14 @@ void RendererOpenGL::RenderScreenshot() {
653 return; 678 return;
654 } 679 }
655 680
681 GLint old_read_fb;
682 GLint old_draw_fb;
683 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
684 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
685
656 // Draw the current frame to the screenshot framebuffer 686 // Draw the current frame to the screenshot framebuffer
657 screenshot_framebuffer.Create(); 687 screenshot_framebuffer.Create();
658 GLuint old_read_fb = state.draw.read_framebuffer; 688 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
659 GLuint old_draw_fb = state.draw.draw_framebuffer;
660 state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
661 state.AllDirty();
662 state.Apply();
663 689
664 Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 690 Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
665 691
@@ -676,12 +702,11 @@ void RendererOpenGL::RenderScreenshot() {
676 renderer_settings.screenshot_bits); 702 renderer_settings.screenshot_bits);
677 703
678 screenshot_framebuffer.Release(); 704 screenshot_framebuffer.Release();
679 state.draw.read_framebuffer = old_read_fb;
680 state.draw.draw_framebuffer = old_draw_fb;
681 state.AllDirty();
682 state.Apply();
683 glDeleteRenderbuffers(1, &renderbuffer); 705 glDeleteRenderbuffers(1, &renderbuffer);
684 706
707 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
708 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
709
685 renderer_settings.screenshot_complete_callback(); 710 renderer_settings.screenshot_complete_callback();
686 renderer_settings.screenshot_requested = false; 711 renderer_settings.screenshot_requested = false;
687} 712}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index d45e69cbc..33073ce5b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,7 +10,8 @@
10#include "common/math_util.h" 10#include "common/math_util.h"
11#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 12#include "video_core/renderer_opengl/gl_resource_manager.h"
13#include "video_core/renderer_opengl/gl_state.h" 13#include "video_core/renderer_opengl/gl_shader_manager.h"
14#include "video_core/renderer_opengl/gl_state_tracker.h"
14 15
15namespace Core { 16namespace Core {
16class System; 17class System;
@@ -76,8 +77,6 @@ private:
76 /// Draws the emulated screens to the emulator window. 77 /// Draws the emulated screens to the emulator window.
77 void DrawScreen(const Layout::FramebufferLayout& layout); 78 void DrawScreen(const Layout::FramebufferLayout& layout);
78 79
79 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
80
81 void RenderScreenshot(); 80 void RenderScreenshot();
82 81
83 /// Loads framebuffer from emulated memory into the active OpenGL texture. 82 /// Loads framebuffer from emulated memory into the active OpenGL texture.
@@ -93,17 +92,20 @@ private:
93 Core::Frontend::EmuWindow& emu_window; 92 Core::Frontend::EmuWindow& emu_window;
94 Core::System& system; 93 Core::System& system;
95 94
96 OpenGLState state; 95 StateTracker state_tracker{system};
97 96
98 // OpenGL object IDs 97 // OpenGL object IDs
99 OGLVertexArray vertex_array;
100 OGLBuffer vertex_buffer; 98 OGLBuffer vertex_buffer;
101 OGLProgram shader; 99 OGLProgram vertex_program;
100 OGLProgram fragment_program;
102 OGLFramebuffer screenshot_framebuffer; 101 OGLFramebuffer screenshot_framebuffer;
103 102
104 /// Display information for Switch screen 103 /// Display information for Switch screen
105 ScreenInfo screen_info; 104 ScreenInfo screen_info;
106 105
106 /// Global dummy shader pipeline
107 GLShader::ProgramManager program_manager;
108
107 /// OpenGL framebuffer data 109 /// OpenGL framebuffer data
108 std::vector<u8> gl_framebuffer_data; 110 std::vector<u8> gl_framebuffer_data;
109 111
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp
index ac99e6385..b751086fa 100644
--- a/src/video_core/renderer_opengl/utils.cpp
+++ b/src/video_core/renderer_opengl/utils.cpp
@@ -9,6 +9,7 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/renderer_opengl/gl_state_tracker.h"
12#include "video_core/renderer_opengl/utils.h" 13#include "video_core/renderer_opengl/utils.h"
13 14
14namespace OpenGL { 15namespace OpenGL {
@@ -20,12 +21,12 @@ struct VertexArrayPushBuffer::Entry {
20 GLsizei stride{}; 21 GLsizei stride{};
21}; 22};
22 23
23VertexArrayPushBuffer::VertexArrayPushBuffer() = default; 24VertexArrayPushBuffer::VertexArrayPushBuffer(StateTracker& state_tracker)
25 : state_tracker{state_tracker} {}
24 26
25VertexArrayPushBuffer::~VertexArrayPushBuffer() = default; 27VertexArrayPushBuffer::~VertexArrayPushBuffer() = default;
26 28
27void VertexArrayPushBuffer::Setup(GLuint vao_) { 29void VertexArrayPushBuffer::Setup() {
28 vao = vao_;
29 index_buffer = nullptr; 30 index_buffer = nullptr;
30 vertex_buffers.clear(); 31 vertex_buffers.clear();
31} 32}
@@ -41,13 +42,11 @@ void VertexArrayPushBuffer::SetVertexBuffer(GLuint binding_index, const GLuint*
41 42
42void VertexArrayPushBuffer::Bind() { 43void VertexArrayPushBuffer::Bind() {
43 if (index_buffer) { 44 if (index_buffer) {
44 glVertexArrayElementBuffer(vao, *index_buffer); 45 state_tracker.BindIndexBuffer(*index_buffer);
45 } 46 }
46 47
47 // TODO(Rodrigo): Find a way to ARB_multi_bind this
48 for (const auto& entry : vertex_buffers) { 48 for (const auto& entry : vertex_buffers) {
49 glVertexArrayVertexBuffer(vao, entry.binding_index, *entry.buffer, entry.offset, 49 glBindVertexBuffer(entry.binding_index, *entry.buffer, entry.offset, entry.stride);
50 entry.stride);
51 } 50 }
52} 51}
53 52
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h
index 3ad7c02d4..47ee3177b 100644
--- a/src/video_core/renderer_opengl/utils.h
+++ b/src/video_core/renderer_opengl/utils.h
@@ -11,12 +11,14 @@
11 11
12namespace OpenGL { 12namespace OpenGL {
13 13
14class StateTracker;
15
14class VertexArrayPushBuffer final { 16class VertexArrayPushBuffer final {
15public: 17public:
16 explicit VertexArrayPushBuffer(); 18 explicit VertexArrayPushBuffer(StateTracker& state_tracker);
17 ~VertexArrayPushBuffer(); 19 ~VertexArrayPushBuffer();
18 20
19 void Setup(GLuint vao_); 21 void Setup();
20 22
21 void SetIndexBuffer(const GLuint* buffer); 23 void SetIndexBuffer(const GLuint* buffer);
22 24
@@ -28,7 +30,8 @@ public:
28private: 30private:
29 struct Entry; 31 struct Entry;
30 32
31 GLuint vao{}; 33 StateTracker& state_tracker;
34
32 const GLuint* index_buffer{}; 35 const GLuint* index_buffer{};
33 std::vector<Entry> vertex_buffers; 36 std::vector<Entry> vertex_buffers;
34}; 37};
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 4e3ff231e..2bb376555 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -112,19 +112,18 @@ constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs)
112 const auto& clip = regs.view_volume_clip_control; 112 const auto& clip = regs.view_volume_clip_control;
113 const bool depth_clamp_enabled = clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1; 113 const bool depth_clamp_enabled = clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1;
114 114
115 Maxwell::Cull::FrontFace front_face = regs.cull.front_face; 115 Maxwell::FrontFace front_face = regs.front_face;
116 if (regs.screen_y_control.triangle_rast_flip != 0 && 116 if (regs.screen_y_control.triangle_rast_flip != 0 &&
117 regs.viewport_transform[0].scale_y > 0.0f) { 117 regs.viewport_transform[0].scale_y > 0.0f) {
118 if (front_face == Maxwell::Cull::FrontFace::CounterClockWise) 118 if (front_face == Maxwell::FrontFace::CounterClockWise)
119 front_face = Maxwell::Cull::FrontFace::ClockWise; 119 front_face = Maxwell::FrontFace::ClockWise;
120 else if (front_face == Maxwell::Cull::FrontFace::ClockWise) 120 else if (front_face == Maxwell::FrontFace::ClockWise)
121 front_face = Maxwell::Cull::FrontFace::CounterClockWise; 121 front_face = Maxwell::FrontFace::CounterClockWise;
122 } 122 }
123 123
124 const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne; 124 const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
125 return FixedPipelineState::Rasterizer(regs.cull.enabled, depth_bias_enabled, 125 return FixedPipelineState::Rasterizer(regs.cull_test_enabled, depth_bias_enabled,
126 depth_clamp_enabled, gl_ndc, regs.cull.cull_face, 126 depth_clamp_enabled, gl_ndc, regs.cull_face, front_face);
127 front_face);
128} 127}
129 128
130} // Anonymous namespace 129} // Anonymous namespace
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 87056ef37..4c8ba7f90 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -171,8 +171,8 @@ struct FixedPipelineState {
171 171
172 struct Rasterizer { 172 struct Rasterizer {
173 constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool depth_clamp_enable, 173 constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool depth_clamp_enable,
174 bool ndc_minus_one_to_one, Maxwell::Cull::CullFace cull_face, 174 bool ndc_minus_one_to_one, Maxwell::CullFace cull_face,
175 Maxwell::Cull::FrontFace front_face) 175 Maxwell::FrontFace front_face)
176 : cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable}, 176 : cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable},
177 depth_clamp_enable{depth_clamp_enable}, ndc_minus_one_to_one{ndc_minus_one_to_one}, 177 depth_clamp_enable{depth_clamp_enable}, ndc_minus_one_to_one{ndc_minus_one_to_one},
178 cull_face{cull_face}, front_face{front_face} {} 178 cull_face{cull_face}, front_face{front_face} {}
@@ -182,8 +182,8 @@ struct FixedPipelineState {
182 bool depth_bias_enable; 182 bool depth_bias_enable;
183 bool depth_clamp_enable; 183 bool depth_clamp_enable;
184 bool ndc_minus_one_to_one; 184 bool ndc_minus_one_to_one;
185 Maxwell::Cull::CullFace cull_face; 185 Maxwell::CullFace cull_face;
186 Maxwell::Cull::FrontFace front_face; 186 Maxwell::FrontFace front_face;
187 187
188 std::size_t Hash() const noexcept; 188 std::size_t Hash() const noexcept;
189 189
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 948d67d89..df3ac707c 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -586,24 +586,24 @@ vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor) {
586 return {}; 586 return {};
587} 587}
588 588
589vk::FrontFace FrontFace(Maxwell::Cull::FrontFace front_face) { 589vk::FrontFace FrontFace(Maxwell::FrontFace front_face) {
590 switch (front_face) { 590 switch (front_face) {
591 case Maxwell::Cull::FrontFace::ClockWise: 591 case Maxwell::FrontFace::ClockWise:
592 return vk::FrontFace::eClockwise; 592 return vk::FrontFace::eClockwise;
593 case Maxwell::Cull::FrontFace::CounterClockWise: 593 case Maxwell::FrontFace::CounterClockWise:
594 return vk::FrontFace::eCounterClockwise; 594 return vk::FrontFace::eCounterClockwise;
595 } 595 }
596 UNIMPLEMENTED_MSG("Unimplemented front face={}", static_cast<u32>(front_face)); 596 UNIMPLEMENTED_MSG("Unimplemented front face={}", static_cast<u32>(front_face));
597 return {}; 597 return {};
598} 598}
599 599
600vk::CullModeFlags CullFace(Maxwell::Cull::CullFace cull_face) { 600vk::CullModeFlags CullFace(Maxwell::CullFace cull_face) {
601 switch (cull_face) { 601 switch (cull_face) {
602 case Maxwell::Cull::CullFace::Front: 602 case Maxwell::CullFace::Front:
603 return vk::CullModeFlagBits::eFront; 603 return vk::CullModeFlagBits::eFront;
604 case Maxwell::Cull::CullFace::Back: 604 case Maxwell::CullFace::Back:
605 return vk::CullModeFlagBits::eBack; 605 return vk::CullModeFlagBits::eBack;
606 case Maxwell::Cull::CullFace::FrontAndBack: 606 case Maxwell::CullFace::FrontAndBack:
607 return vk::CullModeFlagBits::eFrontAndBack; 607 return vk::CullModeFlagBits::eFrontAndBack;
608 } 608 }
609 UNIMPLEMENTED_MSG("Unimplemented cull face={}", static_cast<u32>(cull_face)); 609 UNIMPLEMENTED_MSG("Unimplemented cull face={}", static_cast<u32>(cull_face));
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 7e9678b7b..24f6ab544 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -54,9 +54,9 @@ vk::BlendOp BlendEquation(Maxwell::Blend::Equation equation);
54 54
55vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor); 55vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor);
56 56
57vk::FrontFace FrontFace(Maxwell::Cull::FrontFace front_face); 57vk::FrontFace FrontFace(Maxwell::FrontFace front_face);
58 58
59vk::CullModeFlags CullFace(Maxwell::Cull::CullFace cull_face); 59vk::CullModeFlags CullFace(Maxwell::CullFace cull_face);
60 60
61vk::ComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle); 61vk::ComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
62 62
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ddc62bc97..42bb01418 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -27,6 +27,7 @@
27#include "video_core/renderer_vulkan/vk_rasterizer.h" 27#include "video_core/renderer_vulkan/vk_rasterizer.h"
28#include "video_core/renderer_vulkan/vk_resource_manager.h" 28#include "video_core/renderer_vulkan/vk_resource_manager.h"
29#include "video_core/renderer_vulkan/vk_scheduler.h" 29#include "video_core/renderer_vulkan/vk_scheduler.h"
30#include "video_core/renderer_vulkan/vk_state_tracker.h"
30#include "video_core/renderer_vulkan/vk_swapchain.h" 31#include "video_core/renderer_vulkan/vk_swapchain.h"
31 32
32namespace Vulkan { 33namespace Vulkan {
@@ -177,10 +178,13 @@ bool RendererVulkan::Init() {
177 swapchain = std::make_unique<VKSwapchain>(surface, *device); 178 swapchain = std::make_unique<VKSwapchain>(surface, *device);
178 swapchain->Create(framebuffer.width, framebuffer.height, false); 179 swapchain->Create(framebuffer.width, framebuffer.height, false);
179 180
180 scheduler = std::make_unique<VKScheduler>(*device, *resource_manager); 181 state_tracker = std::make_unique<StateTracker>(system);
182
183 scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker);
181 184
182 rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device, 185 rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
183 *resource_manager, *memory_manager, *scheduler); 186 *resource_manager, *memory_manager,
187 *state_tracker, *scheduler);
184 188
185 blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device, 189 blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
186 *resource_manager, *memory_manager, *swapchain, 190 *resource_manager, *memory_manager, *swapchain,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index f513397f0..3da08d2e4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -4,8 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
10
9#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
10#include "video_core/renderer_vulkan/declarations.h" 12#include "video_core/renderer_vulkan/declarations.h"
11 13
@@ -15,6 +17,7 @@ class System;
15 17
16namespace Vulkan { 18namespace Vulkan {
17 19
20class StateTracker;
18class VKBlitScreen; 21class VKBlitScreen;
19class VKDevice; 22class VKDevice;
20class VKFence; 23class VKFence;
@@ -61,6 +64,7 @@ private:
61 std::unique_ptr<VKSwapchain> swapchain; 64 std::unique_ptr<VKSwapchain> swapchain;
62 std::unique_ptr<VKMemoryManager> memory_manager; 65 std::unique_ptr<VKMemoryManager> memory_manager;
63 std::unique_ptr<VKResourceManager> resource_manager; 66 std::unique_ptr<VKResourceManager> resource_manager;
67 std::unique_ptr<StateTracker> state_tracker;
64 std::unique_ptr<VKScheduler> scheduler; 68 std::unique_ptr<VKScheduler> scheduler;
65 std::unique_ptr<VKBlitScreen> blit_screen; 69 std::unique_ptr<VKBlitScreen> blit_screen;
66}; 70};
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 696e4b291..144e1e007 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -188,11 +188,6 @@ VKPipelineCache::~VKPipelineCache() = default;
188 188
189std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { 189std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
190 const auto& gpu = system.GPU().Maxwell3D(); 190 const auto& gpu = system.GPU().Maxwell3D();
191 auto& dirty = system.GPU().Maxwell3D().dirty.shaders;
192 if (!dirty) {
193 return last_shaders;
194 }
195 dirty = false;
196 191
197 std::array<Shader, Maxwell::MaxShaderProgram> shaders; 192 std::array<Shader, Maxwell::MaxShaderProgram> shaders;
198 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 193 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 3d9fd0e6c..2bcb17b56 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -36,6 +36,7 @@
36#include "video_core/renderer_vulkan/vk_sampler_cache.h" 36#include "video_core/renderer_vulkan/vk_sampler_cache.h"
37#include "video_core/renderer_vulkan/vk_scheduler.h" 37#include "video_core/renderer_vulkan/vk_scheduler.h"
38#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 38#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
39#include "video_core/renderer_vulkan/vk_state_tracker.h"
39#include "video_core/renderer_vulkan/vk_texture_cache.h" 40#include "video_core/renderer_vulkan/vk_texture_cache.h"
40#include "video_core/renderer_vulkan/vk_update_descriptor.h" 41#include "video_core/renderer_vulkan/vk_update_descriptor.h"
41 42
@@ -280,10 +281,11 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf,
280RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& renderer, 281RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& renderer,
281 VKScreenInfo& screen_info, const VKDevice& device, 282 VKScreenInfo& screen_info, const VKDevice& device,
282 VKResourceManager& resource_manager, 283 VKResourceManager& resource_manager,
283 VKMemoryManager& memory_manager, VKScheduler& scheduler) 284 VKMemoryManager& memory_manager, StateTracker& state_tracker,
285 VKScheduler& scheduler)
284 : RasterizerAccelerated{system.Memory()}, system{system}, render_window{renderer}, 286 : RasterizerAccelerated{system.Memory()}, system{system}, render_window{renderer},
285 screen_info{screen_info}, device{device}, resource_manager{resource_manager}, 287 screen_info{screen_info}, device{device}, resource_manager{resource_manager},
286 memory_manager{memory_manager}, scheduler{scheduler}, 288 memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler},
287 staging_pool(device, memory_manager, scheduler), descriptor_pool(device), 289 staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
288 update_descriptor_queue(device, scheduler), 290 update_descriptor_queue(device, scheduler),
289 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 291 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
@@ -548,6 +550,10 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
548 return true; 550 return true;
549} 551}
550 552
553void RasterizerVulkan::SetupDirtyFlags() {
554 state_tracker.Initialize();
555}
556
551void RasterizerVulkan::FlushWork() { 557void RasterizerVulkan::FlushWork() {
552 static constexpr u32 DRAWS_TO_DISPATCH = 4096; 558 static constexpr u32 DRAWS_TO_DISPATCH = 4096;
553 559
@@ -571,9 +577,9 @@ void RasterizerVulkan::FlushWork() {
571 577
572RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { 578RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
573 MICROPROFILE_SCOPE(Vulkan_RenderTargets); 579 MICROPROFILE_SCOPE(Vulkan_RenderTargets);
574 auto& dirty = system.GPU().Maxwell3D().dirty; 580 auto& dirty = system.GPU().Maxwell3D().dirty.flags;
575 const bool update_rendertargets = dirty.render_settings; 581 const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets];
576 dirty.render_settings = false; 582 dirty[VideoCommon::Dirty::RenderTargets] = false;
577 583
578 texture_cache.GuardRenderTargets(true); 584 texture_cache.GuardRenderTargets(true);
579 585
@@ -723,13 +729,13 @@ void RasterizerVulkan::SetupImageTransitions(
723} 729}
724 730
725void RasterizerVulkan::UpdateDynamicStates() { 731void RasterizerVulkan::UpdateDynamicStates() {
726 auto& gpu = system.GPU().Maxwell3D(); 732 auto& regs = system.GPU().Maxwell3D().regs;
727 UpdateViewportsState(gpu); 733 UpdateViewportsState(regs);
728 UpdateScissorsState(gpu); 734 UpdateScissorsState(regs);
729 UpdateDepthBias(gpu); 735 UpdateDepthBias(regs);
730 UpdateBlendConstants(gpu); 736 UpdateBlendConstants(regs);
731 UpdateDepthBounds(gpu); 737 UpdateDepthBounds(regs);
732 UpdateStencilFaces(gpu); 738 UpdateStencilFaces(regs);
733} 739}
734 740
735void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 741void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
@@ -909,6 +915,13 @@ void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) {
909 915
910void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry, 916void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry,
911 const Tegra::Engines::ConstBufferInfo& buffer) { 917 const Tegra::Engines::ConstBufferInfo& buffer) {
918 if (!buffer.enabled) {
919 // Set values to zero to unbind buffers
920 update_descriptor_queue.AddBuffer(buffer_cache.GetEmptyBuffer(sizeof(float)), 0,
921 sizeof(float));
922 return;
923 }
924
912 // Align the size to avoid bad std140 interactions 925 // Align the size to avoid bad std140 interactions
913 const std::size_t size = 926 const std::size_t size =
914 Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); 927 Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float));
@@ -979,12 +992,10 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
979 image_views.push_back(ImageView{std::move(view), image_layout}); 992 image_views.push_back(ImageView{std::move(view), image_layout});
980} 993}
981 994
982void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) { 995void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) {
983 if (!gpu.dirty.viewport_transform && scheduler.TouchViewports()) { 996 if (!state_tracker.TouchViewports()) {
984 return; 997 return;
985 } 998 }
986 gpu.dirty.viewport_transform = false;
987 const auto& regs = gpu.regs;
988 const std::array viewports{ 999 const std::array viewports{
989 GetViewportState(device, regs, 0), GetViewportState(device, regs, 1), 1000 GetViewportState(device, regs, 0), GetViewportState(device, regs, 1),
990 GetViewportState(device, regs, 2), GetViewportState(device, regs, 3), 1001 GetViewportState(device, regs, 2), GetViewportState(device, regs, 3),
@@ -999,12 +1010,10 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
999 }); 1010 });
1000} 1011}
1001 1012
1002void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) { 1013void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) {
1003 if (!gpu.dirty.scissor_test && scheduler.TouchScissors()) { 1014 if (!state_tracker.TouchScissors()) {
1004 return; 1015 return;
1005 } 1016 }
1006 gpu.dirty.scissor_test = false;
1007 const auto& regs = gpu.regs;
1008 const std::array scissors = { 1017 const std::array scissors = {
1009 GetScissorState(regs, 0), GetScissorState(regs, 1), GetScissorState(regs, 2), 1018 GetScissorState(regs, 0), GetScissorState(regs, 1), GetScissorState(regs, 2),
1010 GetScissorState(regs, 3), GetScissorState(regs, 4), GetScissorState(regs, 5), 1019 GetScissorState(regs, 3), GetScissorState(regs, 4), GetScissorState(regs, 5),
@@ -1017,46 +1026,39 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
1017 }); 1026 });
1018} 1027}
1019 1028
1020void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) { 1029void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
1021 if (!gpu.dirty.polygon_offset && scheduler.TouchDepthBias()) { 1030 if (!state_tracker.TouchDepthBias()) {
1022 return; 1031 return;
1023 } 1032 }
1024 gpu.dirty.polygon_offset = false;
1025 const auto& regs = gpu.regs;
1026 scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp, 1033 scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp,
1027 factor = regs.polygon_offset_factor](auto cmdbuf, auto& dld) { 1034 factor = regs.polygon_offset_factor](auto cmdbuf, auto& dld) {
1028 cmdbuf.setDepthBias(constant, clamp, factor / 2.0f, dld); 1035 cmdbuf.setDepthBias(constant, clamp, factor / 2.0f, dld);
1029 }); 1036 });
1030} 1037}
1031 1038
1032void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) { 1039void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs) {
1033 if (!gpu.dirty.blend_state && scheduler.TouchBlendConstants()) { 1040 if (!state_tracker.TouchBlendConstants()) {
1034 return; 1041 return;
1035 } 1042 }
1036 gpu.dirty.blend_state = false; 1043 const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
1037 const std::array blend_color = {gpu.regs.blend_color.r, gpu.regs.blend_color.g, 1044 regs.blend_color.a};
1038 gpu.regs.blend_color.b, gpu.regs.blend_color.a};
1039 scheduler.Record([blend_color](auto cmdbuf, auto& dld) { 1045 scheduler.Record([blend_color](auto cmdbuf, auto& dld) {
1040 cmdbuf.setBlendConstants(blend_color.data(), dld); 1046 cmdbuf.setBlendConstants(blend_color.data(), dld);
1041 }); 1047 });
1042} 1048}
1043 1049
1044void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu) { 1050void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs) {
1045 if (!gpu.dirty.depth_bounds_values && scheduler.TouchDepthBounds()) { 1051 if (!state_tracker.TouchDepthBounds()) {
1046 return; 1052 return;
1047 } 1053 }
1048 gpu.dirty.depth_bounds_values = false;
1049 const auto& regs = gpu.regs;
1050 scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]]( 1054 scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
1051 auto cmdbuf, auto& dld) { cmdbuf.setDepthBounds(min, max, dld); }); 1055 auto cmdbuf, auto& dld) { cmdbuf.setDepthBounds(min, max, dld); });
1052} 1056}
1053 1057
1054void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu) { 1058void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
1055 if (!gpu.dirty.stencil_test && scheduler.TouchStencilValues()) { 1059 if (!state_tracker.TouchStencilProperties()) {
1056 return; 1060 return;
1057 } 1061 }
1058 gpu.dirty.stencil_test = false;
1059 const auto& regs = gpu.regs;
1060 if (regs.stencil_two_side_enable) { 1062 if (regs.stencil_two_side_enable) {
1061 // Separate values per face 1063 // Separate values per face
1062 scheduler.Record( 1064 scheduler.Record(
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 4dc8af6e8..96ea05f0a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -96,6 +96,7 @@ struct hash<Vulkan::FramebufferCacheKey> {
96 96
97namespace Vulkan { 97namespace Vulkan {
98 98
99class StateTracker;
99class BufferBindings; 100class BufferBindings;
100 101
101struct ImageView { 102struct ImageView {
@@ -108,7 +109,7 @@ public:
108 explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window, 109 explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window,
109 VKScreenInfo& screen_info, const VKDevice& device, 110 VKScreenInfo& screen_info, const VKDevice& device,
110 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 111 VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
111 VKScheduler& scheduler); 112 StateTracker& state_tracker, VKScheduler& scheduler);
112 ~RasterizerVulkan() override; 113 ~RasterizerVulkan() override;
113 114
114 void Draw(bool is_indexed, bool is_instanced) override; 115 void Draw(bool is_indexed, bool is_instanced) override;
@@ -127,6 +128,7 @@ public:
127 const Tegra::Engines::Fermi2D::Config& copy_config) override; 128 const Tegra::Engines::Fermi2D::Config& copy_config) override;
128 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 129 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
129 u32 pixel_stride) override; 130 u32 pixel_stride) override;
131 void SetupDirtyFlags() override;
130 132
131 /// Maximum supported size that a constbuffer can have in bytes. 133 /// Maximum supported size that a constbuffer can have in bytes.
132 static constexpr std::size_t MaxConstbufferSize = 0x10000; 134 static constexpr std::size_t MaxConstbufferSize = 0x10000;
@@ -215,12 +217,12 @@ private:
215 217
216 void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); 218 void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
217 219
218 void UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu); 220 void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs);
219 void UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu); 221 void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs);
220 void UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu); 222 void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs);
221 void UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu); 223 void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs);
222 void UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu); 224 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
223 void UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu); 225 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
224 226
225 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; 227 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
226 228
@@ -241,6 +243,7 @@ private:
241 const VKDevice& device; 243 const VKDevice& device;
242 VKResourceManager& resource_manager; 244 VKResourceManager& resource_manager;
243 VKMemoryManager& memory_manager; 245 VKMemoryManager& memory_manager;
246 StateTracker& state_tracker;
244 VKScheduler& scheduler; 247 VKScheduler& scheduler;
245 248
246 VKStagingBufferPool staging_pool; 249 VKStagingBufferPool staging_pool;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 92bd6c344..b61d4fe63 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -2,6 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6#include <mutex>
7#include <optional>
8#include <thread>
9#include <utility>
10
5#include "common/assert.h" 11#include "common/assert.h"
6#include "common/microprofile.h" 12#include "common/microprofile.h"
7#include "video_core/renderer_vulkan/declarations.h" 13#include "video_core/renderer_vulkan/declarations.h"
@@ -9,6 +15,7 @@
9#include "video_core/renderer_vulkan/vk_query_cache.h" 15#include "video_core/renderer_vulkan/vk_query_cache.h"
10#include "video_core/renderer_vulkan/vk_resource_manager.h" 16#include "video_core/renderer_vulkan/vk_resource_manager.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h" 17#include "video_core/renderer_vulkan/vk_scheduler.h"
18#include "video_core/renderer_vulkan/vk_state_tracker.h"
12 19
13namespace Vulkan { 20namespace Vulkan {
14 21
@@ -29,9 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
29 last = nullptr; 36 last = nullptr;
30} 37}
31 38
32VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager) 39VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
33 : device{device}, resource_manager{resource_manager}, next_fence{ 40 StateTracker& state_tracker)
34 &resource_manager.CommitFence()} { 41 : device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
42 next_fence{&resource_manager.CommitFence()} {
35 AcquireNewChunk(); 43 AcquireNewChunk();
36 AllocateNewContext(); 44 AllocateNewContext();
37 worker_thread = std::thread(&VKScheduler::WorkerThread, this); 45 worker_thread = std::thread(&VKScheduler::WorkerThread, this);
@@ -157,12 +165,7 @@ void VKScheduler::AllocateNewContext() {
157 165
158void VKScheduler::InvalidateState() { 166void VKScheduler::InvalidateState() {
159 state.graphics_pipeline = nullptr; 167 state.graphics_pipeline = nullptr;
160 state.viewports = false; 168 state_tracker.InvalidateCommandBufferState();
161 state.scissors = false;
162 state.depth_bias = false;
163 state.blend_constants = false;
164 state.depth_bounds = false;
165 state.stencil_values = false;
166} 169}
167 170
168void VKScheduler::EndPendingOperations() { 171void VKScheduler::EndPendingOperations() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 62fd7858b..c7cc291c3 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -17,6 +17,7 @@
17 17
18namespace Vulkan { 18namespace Vulkan {
19 19
20class StateTracker;
20class VKDevice; 21class VKDevice;
21class VKFence; 22class VKFence;
22class VKQueryCache; 23class VKQueryCache;
@@ -43,7 +44,8 @@ private:
43/// OpenGL-like operations on Vulkan command buffers. 44/// OpenGL-like operations on Vulkan command buffers.
44class VKScheduler { 45class VKScheduler {
45public: 46public:
46 explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager); 47 explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
48 StateTracker& state_tracker);
47 ~VKScheduler(); 49 ~VKScheduler();
48 50
49 /// Sends the current execution context to the GPU. 51 /// Sends the current execution context to the GPU.
@@ -74,36 +76,6 @@ public:
74 query_cache = &query_cache_; 76 query_cache = &query_cache_;
75 } 77 }
76 78
77 /// Returns true when viewports have been set in the current command buffer.
78 bool TouchViewports() {
79 return std::exchange(state.viewports, true);
80 }
81
82 /// Returns true when scissors have been set in the current command buffer.
83 bool TouchScissors() {
84 return std::exchange(state.scissors, true);
85 }
86
87 /// Returns true when depth bias have been set in the current command buffer.
88 bool TouchDepthBias() {
89 return std::exchange(state.depth_bias, true);
90 }
91
92 /// Returns true when blend constants have been set in the current command buffer.
93 bool TouchBlendConstants() {
94 return std::exchange(state.blend_constants, true);
95 }
96
97 /// Returns true when depth bounds have been set in the current command buffer.
98 bool TouchDepthBounds() {
99 return std::exchange(state.depth_bounds, true);
100 }
101
102 /// Returns true when stencil values have been set in the current command buffer.
103 bool TouchStencilValues() {
104 return std::exchange(state.stencil_values, true);
105 }
106
107 /// Send work to a separate thread. 79 /// Send work to a separate thread.
108 template <typename T> 80 template <typename T>
109 void Record(T&& command) { 81 void Record(T&& command) {
@@ -217,6 +189,8 @@ private:
217 189
218 const VKDevice& device; 190 const VKDevice& device;
219 VKResourceManager& resource_manager; 191 VKResourceManager& resource_manager;
192 StateTracker& state_tracker;
193
220 VKQueryCache* query_cache = nullptr; 194 VKQueryCache* query_cache = nullptr;
221 195
222 vk::CommandBuffer current_cmdbuf; 196 vk::CommandBuffer current_cmdbuf;
@@ -226,12 +200,6 @@ private:
226 struct State { 200 struct State {
227 std::optional<vk::RenderPassBeginInfo> renderpass; 201 std::optional<vk::RenderPassBeginInfo> renderpass;
228 vk::Pipeline graphics_pipeline; 202 vk::Pipeline graphics_pipeline;
229 bool viewports = false;
230 bool scissors = false;
231 bool depth_bias = false;
232 bool blend_constants = false;
233 bool depth_bounds = false;
234 bool stencil_values = false;
235 } state; 203 } state;
236 204
237 std::unique_ptr<CommandChunk> chunk; 205 std::unique_ptr<CommandChunk> chunk;
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 171d78afc..d9ea3cc21 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -73,7 +73,8 @@ VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_
73VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { 73VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) {
74 const auto usage = 74 const auto usage =
75 vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst | 75 vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst |
76 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer; 76 vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer |
77 vk::BufferUsageFlagBits::eIndexBuffer;
77 const u32 log2 = Common::Log2Ceil64(size); 78 const u32 log2 = Common::Log2Ceil64(size);
78 const vk::BufferCreateInfo buffer_ci({}, 1ULL << log2, usage, vk::SharingMode::eExclusive, 0, 79 const vk::BufferCreateInfo buffer_ci({}, 1ULL << log2, usage, vk::SharingMode::eExclusive, 0,
79 nullptr); 80 nullptr);
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
new file mode 100644
index 000000000..d74e68b63
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -0,0 +1,101 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstddef>
7#include <iterator>
8
9#include "common/common_types.h"
10#include "core/core.h"
11#include "video_core/dirty_flags.h"
12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/gpu.h"
14#include "video_core/renderer_vulkan/vk_state_tracker.h"
15
16#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
17#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / sizeof(u32))
18
19namespace Vulkan {
20
21namespace {
22
23using namespace Dirty;
24using namespace VideoCommon::Dirty;
25using Tegra::Engines::Maxwell3D;
26using Regs = Maxwell3D::Regs;
27using Tables = Maxwell3D::DirtyState::Tables;
28using Table = Maxwell3D::DirtyState::Table;
29using Flags = Maxwell3D::DirtyState::Flags;
30
31Flags MakeInvalidationFlags() {
32 Flags flags{};
33 flags[Viewports] = true;
34 flags[Scissors] = true;
35 flags[DepthBias] = true;
36 flags[BlendConstants] = true;
37 flags[DepthBounds] = true;
38 flags[StencilProperties] = true;
39 return flags;
40}
41
42void SetupDirtyViewports(Tables& tables) {
43 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
44 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
45 tables[0][OFF(viewport_transform_enabled)] = Viewports;
46}
47
48void SetupDirtyScissors(Tables& tables) {
49 FillBlock(tables[0], OFF(scissor_test), NUM(scissor_test), Scissors);
50}
51
52void SetupDirtyDepthBias(Tables& tables) {
53 auto& table = tables[0];
54 table[OFF(polygon_offset_units)] = DepthBias;
55 table[OFF(polygon_offset_clamp)] = DepthBias;
56 table[OFF(polygon_offset_factor)] = DepthBias;
57}
58
59void SetupDirtyBlendConstants(Tables& tables) {
60 FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendConstants);
61}
62
63void SetupDirtyDepthBounds(Tables& tables) {
64 FillBlock(tables[0], OFF(depth_bounds), NUM(depth_bounds), DepthBounds);
65}
66
67void SetupDirtyStencilProperties(Tables& tables) {
68 auto& table = tables[0];
69 table[OFF(stencil_two_side_enable)] = StencilProperties;
70 table[OFF(stencil_front_func_ref)] = StencilProperties;
71 table[OFF(stencil_front_mask)] = StencilProperties;
72 table[OFF(stencil_front_func_mask)] = StencilProperties;
73 table[OFF(stencil_back_func_ref)] = StencilProperties;
74 table[OFF(stencil_back_mask)] = StencilProperties;
75 table[OFF(stencil_back_func_mask)] = StencilProperties;
76}
77
78} // Anonymous namespace
79
80StateTracker::StateTracker(Core::System& system)
81 : system{system}, invalidation_flags{MakeInvalidationFlags()} {}
82
83void StateTracker::Initialize() {
84 auto& dirty = system.GPU().Maxwell3D().dirty;
85 auto& tables = dirty.tables;
86 SetupDirtyRenderTargets(tables);
87 SetupDirtyViewports(tables);
88 SetupDirtyScissors(tables);
89 SetupDirtyDepthBias(tables);
90 SetupDirtyBlendConstants(tables);
91 SetupDirtyDepthBounds(tables);
92 SetupDirtyStencilProperties(tables);
93
94 SetupCommonOnWriteStores(dirty.on_write_stores);
95}
96
97void StateTracker::InvalidateCommandBufferState() {
98 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
99}
100
101} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
new file mode 100644
index 000000000..03bc415b2
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -0,0 +1,79 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <limits>
9
10#include "common/common_types.h"
11#include "core/core.h"
12#include "video_core/dirty_flags.h"
13#include "video_core/engines/maxwell_3d.h"
14
15namespace Vulkan {
16
17namespace Dirty {
18
19enum : u8 {
20 First = VideoCommon::Dirty::LastCommonEntry,
21
22 Viewports,
23 Scissors,
24 DepthBias,
25 BlendConstants,
26 DepthBounds,
27 StencilProperties,
28
29 Last
30};
31static_assert(Last <= std::numeric_limits<u8>::max());
32
33} // namespace Dirty
34
35class StateTracker {
36public:
37 explicit StateTracker(Core::System& system);
38
39 void Initialize();
40
41 void InvalidateCommandBufferState();
42
43 bool TouchViewports() {
44 return Exchange(Dirty::Viewports, false);
45 }
46
47 bool TouchScissors() {
48 return Exchange(Dirty::Scissors, false);
49 }
50
51 bool TouchDepthBias() {
52 return Exchange(Dirty::DepthBias, false);
53 }
54
55 bool TouchBlendConstants() {
56 return Exchange(Dirty::BlendConstants, false);
57 }
58
59 bool TouchDepthBounds() {
60 return Exchange(Dirty::DepthBounds, false);
61 }
62
63 bool TouchStencilProperties() {
64 return Exchange(Dirty::StencilProperties, false);
65 }
66
67private:
68 bool Exchange(std::size_t id, bool new_value) const noexcept {
69 auto& flags = system.GPU().Maxwell3D().dirty.flags;
70 const bool is_dirty = flags[id];
71 flags[id] = new_value;
72 return is_dirty;
73 }
74
75 Core::System& system;
76 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
77};
78
79} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 51b0d38a6..73d92a5ae 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -22,6 +22,7 @@
22#include "video_core/renderer_vulkan/vk_device.h" 22#include "video_core/renderer_vulkan/vk_device.h"
23#include "video_core/renderer_vulkan/vk_memory_manager.h" 23#include "video_core/renderer_vulkan/vk_memory_manager.h"
24#include "video_core/renderer_vulkan/vk_rasterizer.h" 24#include "video_core/renderer_vulkan/vk_rasterizer.h"
25#include "video_core/renderer_vulkan/vk_scheduler.h"
25#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 26#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
26#include "video_core/renderer_vulkan/vk_texture_cache.h" 27#include "video_core/renderer_vulkan/vk_texture_cache.h"
27#include "video_core/surface.h" 28#include "video_core/surface.h"
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index c70e4aec2..51373b687 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -22,6 +22,7 @@
22#include "core/core.h" 22#include "core/core.h"
23#include "core/memory.h" 23#include "core/memory.h"
24#include "core/settings.h" 24#include "core/settings.h"
25#include "video_core/dirty_flags.h"
25#include "video_core/engines/fermi_2d.h" 26#include "video_core/engines/fermi_2d.h"
26#include "video_core/engines/maxwell_3d.h" 27#include "video_core/engines/maxwell_3d.h"
27#include "video_core/gpu.h" 28#include "video_core/gpu.h"
@@ -142,11 +143,10 @@ public:
142 TView GetDepthBufferSurface(bool preserve_contents) { 143 TView GetDepthBufferSurface(bool preserve_contents) {
143 std::lock_guard lock{mutex}; 144 std::lock_guard lock{mutex};
144 auto& maxwell3d = system.GPU().Maxwell3D(); 145 auto& maxwell3d = system.GPU().Maxwell3D();
145 146 if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) {
146 if (!maxwell3d.dirty.depth_buffer) {
147 return depth_buffer.view; 147 return depth_buffer.view;
148 } 148 }
149 maxwell3d.dirty.depth_buffer = false; 149 maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false;
150 150
151 const auto& regs{maxwell3d.regs}; 151 const auto& regs{maxwell3d.regs};
152 const auto gpu_addr{regs.zeta.Address()}; 152 const auto gpu_addr{regs.zeta.Address()};
@@ -175,10 +175,10 @@ public:
175 std::lock_guard lock{mutex}; 175 std::lock_guard lock{mutex};
176 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); 176 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
177 auto& maxwell3d = system.GPU().Maxwell3D(); 177 auto& maxwell3d = system.GPU().Maxwell3D();
178 if (!maxwell3d.dirty.render_target[index]) { 178 if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index]) {
179 return render_targets[index].view; 179 return render_targets[index].view;
180 } 180 }
181 maxwell3d.dirty.render_target[index] = false; 181 maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index] = false;
182 182
183 const auto& regs{maxwell3d.regs}; 183 const auto& regs{maxwell3d.regs};
184 if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || 184 if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
@@ -320,14 +320,14 @@ protected:
320 virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0; 320 virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
321 321
322 void ManageRenderTargetUnregister(TSurface& surface) { 322 void ManageRenderTargetUnregister(TSurface& surface) {
323 auto& maxwell3d = system.GPU().Maxwell3D(); 323 auto& dirty = system.GPU().Maxwell3D().dirty;
324 const u32 index = surface->GetRenderTarget(); 324 const u32 index = surface->GetRenderTarget();
325 if (index == DEPTH_RT) { 325 if (index == DEPTH_RT) {
326 maxwell3d.dirty.depth_buffer = true; 326 dirty.flags[VideoCommon::Dirty::ZetaBuffer] = true;
327 } else { 327 } else {
328 maxwell3d.dirty.render_target[index] = true; 328 dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index] = true;
329 } 329 }
330 maxwell3d.dirty.render_settings = true; 330 dirty.flags[VideoCommon::Dirty::RenderTargets] = true;
331 } 331 }
332 332
333 void Register(TSurface surface) { 333 void Register(TSurface surface) {
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 8e82c6748..7edc4abe1 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -8,6 +8,7 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/settings.h"
11 12
12namespace Tegra::Texture { 13namespace Tegra::Texture {
13 14
@@ -294,6 +295,14 @@ enum class TextureMipmapFilter : u32 {
294 Linear = 3, 295 Linear = 3,
295}; 296};
296 297
298enum class Anisotropy {
299 Default,
300 Filter2x,
301 Filter4x,
302 Filter8x,
303 Filter16x,
304};
305
297struct TSCEntry { 306struct TSCEntry {
298 union { 307 union {
299 struct { 308 struct {
@@ -328,7 +337,22 @@ struct TSCEntry {
328 }; 337 };
329 338
330 float GetMaxAnisotropy() const { 339 float GetMaxAnisotropy() const {
331 return static_cast<float>(1U << max_anisotropy); 340 const u32 min_value = [] {
341 switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) {
342 default:
343 case Anisotropy::Default:
344 return 1U;
345 case Anisotropy::Filter2x:
346 return 2U;
347 case Anisotropy::Filter4x:
348 return 4U;
349 case Anisotropy::Filter8x:
350 return 8U;
351 case Anisotropy::Filter16x:
352 return 16U;
353 }
354 }();
355 return static_cast<float>(std::max(1U << max_anisotropy, min_value));
332 } 356 }
333 357
334 float GetMinLod() const { 358 float GetMinLod() const {
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index b841e63fa..d34b47b3f 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -42,6 +42,9 @@ add_executable(yuzu
42 configuration/configure_graphics.cpp 42 configuration/configure_graphics.cpp
43 configuration/configure_graphics.h 43 configuration/configure_graphics.h
44 configuration/configure_graphics.ui 44 configuration/configure_graphics.ui
45 configuration/configure_graphics_advanced.cpp
46 configuration/configure_graphics_advanced.h
47 configuration/configure_graphics_advanced.ui
45 configuration/configure_hotkeys.cpp 48 configuration/configure_hotkeys.cpp
46 configuration/configure_hotkeys.h 49 configuration/configure_hotkeys.h
47 configuration/configure_hotkeys.ui 50 configuration/configure_hotkeys.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d0f574147..c38860628 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -631,6 +631,7 @@ void Config::ReadRendererValues() {
631 Settings::values.resolution_factor = 631 Settings::values.resolution_factor =
632 ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); 632 ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
633 Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt(); 633 Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt();
634 Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt();
634 Settings::values.use_frame_limit = 635 Settings::values.use_frame_limit =
635 ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); 636 ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
636 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); 637 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
@@ -1067,6 +1068,7 @@ void Config::SaveRendererValues() {
1067 WriteSetting(QStringLiteral("resolution_factor"), 1068 WriteSetting(QStringLiteral("resolution_factor"),
1068 static_cast<double>(Settings::values.resolution_factor), 1.0); 1069 static_cast<double>(Settings::values.resolution_factor), 1.0);
1069 WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); 1070 WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
1071 WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
1070 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); 1072 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
1071 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); 1073 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
1072 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, 1074 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 67b990f1a..9aec1bd09 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -83,6 +83,11 @@
83 <string>Graphics</string> 83 <string>Graphics</string>
84 </attribute> 84 </attribute>
85 </widget> 85 </widget>
86 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
87 <attribute name="title">
88 <string>GraphicsAdvanced</string>
89 </attribute>
90 </widget>
86 <widget class="ConfigureAudio" name="audioTab"> 91 <widget class="ConfigureAudio" name="audioTab">
87 <attribute name="title"> 92 <attribute name="title">
88 <string>Audio</string> 93 <string>Audio</string>
@@ -160,6 +165,12 @@
160 <container>1</container> 165 <container>1</container>
161 </customwidget> 166 </customwidget>
162 <customwidget> 167 <customwidget>
168 <class>ConfigureGraphicsAdvanced</class>
169 <extends>QWidget</extends>
170 <header>configuration/configure_graphics_advanced.h</header>
171 <container>1</container>
172 </customwidget>
173 <customwidget>
163 <class>ConfigureWeb</class> 174 <class>ConfigureWeb</class>
164 <extends>QWidget</extends> 175 <extends>QWidget</extends>
165 <header>configuration/configure_web.h</header> 176 <header>configuration/configure_web.h</header>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index db3b19352..df4473b46 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -41,6 +41,7 @@ void ConfigureDialog::ApplyConfiguration() {
41 ui->inputTab->ApplyConfiguration(); 41 ui->inputTab->ApplyConfiguration();
42 ui->hotkeysTab->ApplyConfiguration(registry); 42 ui->hotkeysTab->ApplyConfiguration(registry);
43 ui->graphicsTab->ApplyConfiguration(); 43 ui->graphicsTab->ApplyConfiguration();
44 ui->graphicsAdvancedTab->ApplyConfiguration();
44 ui->audioTab->ApplyConfiguration(); 45 ui->audioTab->ApplyConfiguration();
45 ui->debugTab->ApplyConfiguration(); 46 ui->debugTab->ApplyConfiguration();
46 ui->webTab->ApplyConfiguration(); 47 ui->webTab->ApplyConfiguration();
@@ -76,7 +77,7 @@ void ConfigureDialog::PopulateSelectionList() {
76 const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ 77 const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
77 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, 78 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
78 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 79 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
79 {tr("Graphics"), {ui->graphicsTab}}, 80 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
80 {tr("Audio"), {ui->audioTab}}, 81 {tr("Audio"), {ui->audioTab}},
81 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 82 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
82 }; 83 };
@@ -105,6 +106,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
105 {ui->inputTab, tr("Input")}, 106 {ui->inputTab, tr("Input")},
106 {ui->hotkeysTab, tr("Hotkeys")}, 107 {ui->hotkeysTab, tr("Hotkeys")},
107 {ui->graphicsTab, tr("Graphics")}, 108 {ui->graphicsTab, tr("Graphics")},
109 {ui->graphicsAdvancedTab, tr("Advanced")},
108 {ui->audioTab, tr("Audio")}, 110 {ui->audioTab, tr("Audio")},
109 {ui->debugTab, tr("Debug")}, 111 {ui->debugTab, tr("Debug")},
110 {ui->webTab, tr("Web")}, 112 {ui->webTab, tr("Web")},
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index fe64c7d81..a821c7b3c 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -100,13 +100,8 @@ void ConfigureGraphics::SetConfiguration() {
100 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio); 100 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
101 ui->use_disk_shader_cache->setEnabled(runtime_lock); 101 ui->use_disk_shader_cache->setEnabled(runtime_lock);
102 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); 102 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
103 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
104 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 103 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
105 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 104 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
106 ui->use_vsync->setEnabled(runtime_lock);
107 ui->use_vsync->setChecked(Settings::values.use_vsync);
108 ui->force_30fps_mode->setEnabled(runtime_lock);
109 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
110 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 105 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
111 Settings::values.bg_blue)); 106 Settings::values.bg_blue));
112 UpdateDeviceComboBox(); 107 UpdateDeviceComboBox();
@@ -119,11 +114,8 @@ void ConfigureGraphics::ApplyConfiguration() {
119 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 114 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
120 Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex(); 115 Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex();
121 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); 116 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
122 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
123 Settings::values.use_asynchronous_gpu_emulation = 117 Settings::values.use_asynchronous_gpu_emulation =
124 ui->use_asynchronous_gpu_emulation->isChecked(); 118 ui->use_asynchronous_gpu_emulation->isChecked();
125 Settings::values.use_vsync = ui->use_vsync->isChecked();
126 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
127 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 119 Settings::values.bg_red = static_cast<float>(bg_color.redF());
128 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 120 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
129 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 121 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 9acc7dd93..c816d6108 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -85,30 +85,6 @@
85 </widget> 85 </widget>
86 </item> 86 </item>
87 <item> 87 <item>
88 <widget class="QCheckBox" name="use_vsync">
89 <property name="toolTip">
90 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
91 </property>
92 <property name="text">
93 <string>Use VSync (OpenGL only)</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
99 <property name="text">
100 <string>Use accurate GPU emulation (slow)</string>
101 </property>
102 </widget>
103 </item>
104 <item>
105 <widget class="QCheckBox" name="force_30fps_mode">
106 <property name="text">
107 <string>Force 30 FPS mode</string>
108 </property>
109 </widget>
110 </item>
111 <item>
112 <layout class="QHBoxLayout" name="horizontalLayout_2"> 88 <layout class="QHBoxLayout" name="horizontalLayout_2">
113 <item> 89 <item>
114 <widget class="QLabel" name="label"> 90 <widget class="QLabel" name="label">
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
new file mode 100644
index 000000000..b9f429f84
--- /dev/null
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -0,0 +1,48 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/settings.h"
7#include "ui_configure_graphics_advanced.h"
8#include "yuzu/configuration/configure_graphics_advanced.h"
9
10ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
11 : QWidget(parent), ui(new Ui::ConfigureGraphicsAdvanced) {
12
13 ui->setupUi(this);
14
15 SetConfiguration();
16}
17
18ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
19
20void ConfigureGraphicsAdvanced::SetConfiguration() {
21 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
22 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
23 ui->use_vsync->setEnabled(runtime_lock);
24 ui->use_vsync->setChecked(Settings::values.use_vsync);
25 ui->force_30fps_mode->setEnabled(runtime_lock);
26 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
27 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
28 ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy);
29}
30
31void ConfigureGraphicsAdvanced::ApplyConfiguration() {
32 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
33 Settings::values.use_vsync = ui->use_vsync->isChecked();
34 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
35 Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
36}
37
38void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
39 if (event->type() == QEvent::LanguageChange) {
40 RetranslateUI();
41 }
42
43 QWidget::changeEvent(event);
44}
45
46void ConfigureGraphicsAdvanced::RetranslateUI() {
47 ui->retranslateUi(this);
48}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
new file mode 100644
index 000000000..bbc9d4355
--- /dev/null
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -0,0 +1,30 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureGraphicsAdvanced;
12}
13
14class ConfigureGraphicsAdvanced : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureGraphicsAdvanced(QWidget* parent = nullptr);
19 ~ConfigureGraphicsAdvanced() override;
20
21 void ApplyConfiguration();
22
23private:
24 void changeEvent(QEvent* event) override;
25 void RetranslateUI();
26
27 void SetConfiguration();
28
29 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
30};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
new file mode 100644
index 000000000..42eec278e
--- /dev/null
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -0,0 +1,111 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureGraphicsAdvanced</class>
4 <widget class="QWidget" name="ConfigureGraphicsAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>321</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_1">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_2">
19 <item>
20 <widget class="QGroupBox" name="groupBox_1">
21 <property name="title">
22 <string>Advanced Graphics Settings</string>
23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item>
26 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
27 <property name="text">
28 <string>Use accurate GPU emulation (slow)</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <widget class="QCheckBox" name="use_vsync">
34 <property name="toolTip">
35 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
36 </property>
37 <property name="text">
38 <string>Use VSync (OpenGL only)</string>
39 </property>
40 </widget>
41 </item>
42 <item>
43 <widget class="QCheckBox" name="force_30fps_mode">
44 <property name="text">
45 <string>Force 30 FPS mode</string>
46 </property>
47 </widget>
48 </item>
49 <item>
50 <layout class="QHBoxLayout" name="horizontalLayout_1">
51 <item>
52 <widget class="QLabel" name="af_label">
53 <property name="text">
54 <string>Anisotropic Filtering:</string>
55 </property>
56 </widget>
57 </item>
58 <item>
59 <widget class="QComboBox" name="anisotropic_filtering_combobox">
60 <item>
61 <property name="text">
62 <string>Default</string>
63 </property>
64 </item>
65 <item>
66 <property name="text">
67 <string>2x</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>4x</string>
73 </property>
74 </item>
75 <item>
76 <property name="text">
77 <string>8x</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string>16x</string>
83 </property>
84 </item>
85 </widget>
86 </item>
87 </layout>
88 </item>
89 </layout>
90 </widget>
91 </item>
92 </layout>
93 </item>
94 <item>
95 <spacer name="verticalSpacer">
96 <property name="orientation">
97 <enum>Qt::Vertical</enum>
98 </property>
99 <property name="sizeHint" stdset="0">
100 <size>
101 <width>20</width>
102 <height>40</height>
103 </size>
104 </property>
105 </spacer>
106 </item>
107 </layout>
108 </widget>
109 <resources/>
110 <connections/>
111</ui>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index b77c12baf..907abaa51 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -381,6 +381,8 @@ void Config::ReadValues() {
381 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); 381 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
382 Settings::values.aspect_ratio = 382 Settings::values.aspect_ratio =
383 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); 383 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0));
384 Settings::values.max_anisotropy =
385 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0));
384 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 386 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
385 Settings::values.frame_limit = 387 Settings::values.frame_limit =
386 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 388 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 085ffbc81..d63d7a58e 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -126,6 +126,10 @@ resolution_factor =
126# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window 126# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
127aspect_ratio = 127aspect_ratio =
128 128
129# Anisotropic filtering
130# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
131max_anisotropy =
132
129# Whether to enable V-Sync (caps the framerate at 60FPS) or not. 133# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
130# 0 (default): Off, 1: On 134# 0 (default): Off, 1: On
131use_vsync = 135use_vsync =
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index 0ac93b62a..ee2591c8f 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -120,6 +120,8 @@ void Config::ReadValues() {
120 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); 120 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
121 Settings::values.aspect_ratio = 121 Settings::values.aspect_ratio =
122 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); 122 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0));
123 Settings::values.max_anisotropy =
124 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0));
123 Settings::values.use_frame_limit = false; 125 Settings::values.use_frame_limit = false;
124 Settings::values.frame_limit = 100; 126 Settings::values.frame_limit = 100;
125 Settings::values.use_disk_shader_cache = 127 Settings::values.use_disk_shader_cache =
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h
index 8d93f7b88..ca203b64d 100644
--- a/src/yuzu_tester/default_ini.h
+++ b/src/yuzu_tester/default_ini.h
@@ -30,6 +30,10 @@ resolution_factor =
30# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window 30# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
31aspect_ratio = 31aspect_ratio =
32 32
33# Anisotropic filtering
34# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
35max_anisotropy =
36
33# Whether to enable V-Sync (caps the framerate at 60FPS) or not. 37# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
34# 0 (default): Off, 1: On 38# 0 (default): Off, 1: On
35use_vsync = 39use_vsync =