summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/engines/maxwell_3d.cpp7
-rw-r--r--src/video_core/engines/maxwell_3d.h142
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp159
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h12
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp152
-rw-r--r--src/video_core/renderer_opengl/gl_state.h32
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h6
-rw-r--r--src/video_core/textures/texture.h15
9 files changed, 315 insertions, 211 deletions
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 6de07ea56..a04e00ecb 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is 34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS. 35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { 36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f; 37 regs.viewports[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f; 38 regs.viewports[viewport].depth_range_far = 1.0f;
39 } 39 }
40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend 40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
41 // so initialize blend registers with sane values 41 // so initialize blend registers with sane values
@@ -66,6 +66,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
66 regs.stencil_back_func_func = Regs::ComparisonOp::Always; 66 regs.stencil_back_func_func = Regs::ComparisonOp::Always;
67 regs.stencil_back_func_mask = 0xFFFFFFFF; 67 regs.stencil_back_func_mask = 0xFFFFFFFF;
68 regs.stencil_back_mask = 0xFFFFFFFF; 68 regs.stencil_back_mask = 0xFFFFFFFF;
69 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
70 // register carrying a default value. Assume it's OpenGL's default (1).
71 regs.point_size = 1.0f;
69} 72}
70 73
71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 74void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 91ca57883..9e480dc39 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -480,6 +480,67 @@ public:
480 }; 480 };
481 }; 481 };
482 482
483 struct ViewportTransform {
484 f32 scale_x;
485 f32 scale_y;
486 f32 scale_z;
487 f32 translate_x;
488 f32 translate_y;
489 f32 translate_z;
490 INSERT_PADDING_WORDS(2);
491
492 MathUtil::Rectangle<s32> GetRect() const {
493 return {
494 GetX(), // left
495 GetY() + GetHeight(), // top
496 GetX() + GetWidth(), // right
497 GetY() // bottom
498 };
499 };
500
501 s32 GetX() const {
502 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
503 }
504
505 s32 GetY() const {
506 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
507 }
508
509 s32 GetWidth() const {
510 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
511 }
512
513 s32 GetHeight() const {
514 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
515 }
516 };
517
518 struct ScissorTest {
519 u32 enable;
520 union {
521 BitField<0, 16, u32> min_x;
522 BitField<16, 16, u32> max_x;
523 };
524 union {
525 BitField<0, 16, u32> min_y;
526 BitField<16, 16, u32> max_y;
527 };
528 u32 fill;
529 };
530
531 struct ViewPort {
532 union {
533 BitField<0, 16, u32> x;
534 BitField<16, 16, u32> width;
535 };
536 union {
537 BitField<0, 16, u32> y;
538 BitField<16, 16, u32> height;
539 };
540 float depth_range_near;
541 float depth_range_far;
542 };
543
483 bool IsShaderConfigEnabled(std::size_t index) const { 544 bool IsShaderConfigEnabled(std::size_t index) const {
484 // The VertexB is always enabled. 545 // The VertexB is always enabled.
485 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { 546 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -505,55 +566,11 @@ public:
505 566
506 INSERT_PADDING_WORDS(0x2E); 567 INSERT_PADDING_WORDS(0x2E);
507 568
508 RenderTargetConfig rt[NumRenderTargets]; 569 std::array<RenderTargetConfig, NumRenderTargets> rt;
509
510 struct {
511 f32 scale_x;
512 f32 scale_y;
513 f32 scale_z;
514 f32 translate_x;
515 f32 translate_y;
516 f32 translate_z;
517 INSERT_PADDING_WORDS(2);
518
519 MathUtil::Rectangle<s32> GetRect() const {
520 return {
521 GetX(), // left
522 GetY() + GetHeight(), // top
523 GetX() + GetWidth(), // right
524 GetY() // bottom
525 };
526 };
527
528 s32 GetX() const {
529 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
530 }
531 570
532 s32 GetY() const { 571 std::array<ViewportTransform, NumViewports> viewport_transform;
533 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
534 }
535 572
536 s32 GetWidth() const { 573 std::array<ViewPort, NumViewports> viewports;
537 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
538 }
539
540 s32 GetHeight() const {
541 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
542 }
543 } viewport_transform[NumViewports];
544
545 struct {
546 union {
547 BitField<0, 16, u32> x;
548 BitField<16, 16, u32> width;
549 };
550 union {
551 BitField<0, 16, u32> y;
552 BitField<16, 16, u32> height;
553 };
554 float depth_range_near;
555 float depth_range_far;
556 } viewport[NumViewports];
557 574
558 INSERT_PADDING_WORDS(0x1D); 575 INSERT_PADDING_WORDS(0x1D);
559 576
@@ -571,19 +588,9 @@ public:
571 588
572 INSERT_PADDING_WORDS(0x17); 589 INSERT_PADDING_WORDS(0x17);
573 590
574 struct { 591 std::array<ScissorTest, NumViewports> scissor_test;
575 u32 enable;
576 union {
577 BitField<0, 16, u32> min_x;
578 BitField<16, 16, u32> max_x;
579 };
580 union {
581 BitField<0, 16, u32> min_y;
582 BitField<16, 16, u32> max_y;
583 };
584 } scissor_test;
585 592
586 INSERT_PADDING_WORDS(0x52); 593 INSERT_PADDING_WORDS(0x15);
587 594
588 s32 stencil_back_func_ref; 595 s32 stencil_back_func_ref;
589 u32 stencil_back_mask; 596 u32 stencil_back_mask;
@@ -700,7 +707,9 @@ public:
700 u32 stencil_front_func_mask; 707 u32 stencil_front_func_mask;
701 u32 stencil_front_mask; 708 u32 stencil_front_mask;
702 709
703 INSERT_PADDING_WORDS(0x3); 710 INSERT_PADDING_WORDS(0x2);
711
712 u32 frag_color_clamp;
704 713
705 union { 714 union {
706 BitField<4, 1, u32> triangle_rast_flip; 715 BitField<4, 1, u32> triangle_rast_flip;
@@ -718,7 +727,12 @@ public:
718 727
719 u32 zeta_enable; 728 u32 zeta_enable;
720 729
721 INSERT_PADDING_WORDS(0x8); 730 union {
731 BitField<0, 1, u32> alpha_to_coverage;
732 BitField<4, 1, u32> alpha_to_one;
733 } multisample_control;
734
735 INSERT_PADDING_WORDS(0x7);
722 736
723 struct { 737 struct {
724 u32 tsc_address_high; 738 u32 tsc_address_high;
@@ -1100,8 +1114,8 @@ private:
1100ASSERT_REG_POSITION(macros, 0x45); 1114ASSERT_REG_POSITION(macros, 0x45);
1101ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 1115ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
1102ASSERT_REG_POSITION(rt, 0x200); 1116ASSERT_REG_POSITION(rt, 0x200);
1103ASSERT_REG_POSITION(viewport_transform[0], 0x280); 1117ASSERT_REG_POSITION(viewport_transform, 0x280);
1104ASSERT_REG_POSITION(viewport, 0x300); 1118ASSERT_REG_POSITION(viewports, 0x300);
1105ASSERT_REG_POSITION(vertex_buffer, 0x35D); 1119ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1106ASSERT_REG_POSITION(clear_color[0], 0x360); 1120ASSERT_REG_POSITION(clear_color[0], 0x360);
1107ASSERT_REG_POSITION(clear_depth, 0x364); 1121ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
1136ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); 1150ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
1137ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); 1151ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
1138ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 1152ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1153ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1139ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1154ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1140ASSERT_REG_POSITION(vb_element_base, 0x50D); 1155ASSERT_REG_POSITION(vb_element_base, 0x50D);
1141ASSERT_REG_POSITION(point_size, 0x546); 1156ASSERT_REG_POSITION(point_size, 0x546);
1142ASSERT_REG_POSITION(zeta_enable, 0x54E); 1157ASSERT_REG_POSITION(zeta_enable, 0x54E);
1158ASSERT_REG_POSITION(multisample_control, 0x54F);
1143ASSERT_REG_POSITION(tsc, 0x557); 1159ASSERT_REG_POSITION(tsc, 0x557);
1144ASSERT_REG_POSITION(tic, 0x55D); 1160ASSERT_REG_POSITION(tic, 0x55D);
1145ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); 1161ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84bd91eed..ae6aaee4c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -580,6 +580,8 @@ void RasterizerOpenGL::DrawArrays() {
580 580
581 ConfigureFramebuffers(state); 581 ConfigureFramebuffers(state);
582 SyncColorMask(); 582 SyncColorMask();
583 SyncFragmentColorClampState();
584 SyncMultiSampleState();
583 SyncDepthTestState(); 585 SyncDepthTestState();
584 SyncStencilTestState(); 586 SyncStencilTestState();
585 SyncBlendState(); 587 SyncBlendState();
@@ -640,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
640 params.DispatchDraw(); 642 params.DispatchDraw();
641 643
642 // Disable scissor test 644 // Disable scissor test
643 state.scissor.enabled = false; 645 state.viewports[0].scissor.enabled = false;
644 646
645 accelerate_draw = AccelDraw::Disabled; 647 accelerate_draw = AccelDraw::Disabled;
646 648
@@ -731,9 +733,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
731 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); 733 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
732} 734}
733 735
734void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) { 736void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
735 const GLuint s = sampler.handle; 737 const GLuint s = sampler.handle;
736 const Tegra::Texture::TSCEntry& config = info.tsc;
737 if (mag_filter != config.mag_filter) { 738 if (mag_filter != config.mag_filter) {
738 mag_filter = config.mag_filter; 739 mag_filter = config.mag_filter;
739 glSamplerParameteri( 740 glSamplerParameteri(
@@ -775,30 +776,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex
775 MaxwellToGL::DepthCompareFunc(depth_compare_func)); 776 MaxwellToGL::DepthCompareFunc(depth_compare_func));
776 } 777 }
777 778
778 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || 779 GLvec4 new_border_color;
779 wrap_p == Tegra::Texture::WrapMode::Border) { 780 if (config.srgb_conversion) {
780 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, 781 new_border_color[0] = config.srgb_border_color_r / 255.0f;
781 config.border_color_b, config.border_color_a}}; 782 new_border_color[1] = config.srgb_border_color_g / 255.0f;
782 if (border_color != new_border_color) { 783 new_border_color[2] = config.srgb_border_color_g / 255.0f;
783 border_color = new_border_color; 784 } else {
784 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 785 new_border_color[0] = config.border_color_r;
785 } 786 new_border_color[1] = config.border_color_g;
787 new_border_color[2] = config.border_color_b;
786 } 788 }
787 if (info.tic.use_header_opt_control == 0) { 789 new_border_color[3] = config.border_color_a;
790
791 if (border_color != new_border_color) {
792 border_color = new_border_color;
793 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
794 }
795
796 const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
797 if (anisotropic_max != max_anisotropic) {
798 max_anisotropic = anisotropic_max;
788 if (GLAD_GL_ARB_texture_filter_anisotropic) { 799 if (GLAD_GL_ARB_texture_filter_anisotropic) {
789 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, 800 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
790 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
791 } else if (GLAD_GL_EXT_texture_filter_anisotropic) { 801 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
792 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 802 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
793 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
794 } 803 }
795 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, 804 }
796 static_cast<float>(info.tic.res_min_mip_level.Value())); 805 const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
797 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, 806 if (lod_min != min_lod) {
798 static_cast<float>(info.tic.res_max_mip_level.Value() == 0 807 min_lod = lod_min;
799 ? 16 808 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
800 : info.tic.res_max_mip_level.Value())); 809 }
801 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f); 810
811 const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
812 if (lod_max != max_lod) {
813 max_lod = lod_max;
814 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
815 }
816 const u32 bias = config.mip_lod_bias.Value();
817 // Sign extend the 13-bit value.
818 const u32 mask = 1U << (13 - 1);
819 const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
820 if (lod_bias != bias_lod) {
821 lod_bias = bias_lod;
822 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
802 } 823 }
803} 824}
804 825
@@ -897,7 +918,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
897 continue; 918 continue;
898 } 919 }
899 920
900 texture_samplers[current_bindpoint].SyncWithConfig(texture); 921 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
901 Surface surface = res_cache.GetTextureSurface(texture, entry); 922 Surface surface = res_cache.GetTextureSurface(texture, entry);
902 if (surface != nullptr) { 923 if (surface != nullptr) {
903 state.texture_units[current_bindpoint].texture = surface->Texture().handle; 924 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -921,15 +942,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
921 942
922void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 943void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
923 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 944 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
924 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 945 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
925 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; 946 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
926 auto& viewport = current_state.viewports[i]; 947 auto& viewport = current_state.viewports[i];
927 viewport.x = viewport_rect.left; 948 viewport.x = viewport_rect.left;
928 viewport.y = viewport_rect.bottom; 949 viewport.y = viewport_rect.bottom;
929 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); 950 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
930 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); 951 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
931 viewport.depth_range_far = regs.viewport[i].depth_range_far; 952 viewport.depth_range_far = regs.viewports[i].depth_range_far;
932 viewport.depth_range_near = regs.viewport[i].depth_range_near; 953 viewport.depth_range_near = regs.viewports[i].depth_range_near;
933 } 954 }
934} 955}
935 956
@@ -1020,7 +1041,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
1020 1041
1021void RasterizerOpenGL::SyncColorMask() { 1042void RasterizerOpenGL::SyncColorMask() {
1022 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1043 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1023 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1044 const std::size_t count =
1045 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
1046 for (std::size_t i = 0; i < count; i++) {
1024 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i]; 1047 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
1025 auto& dest = state.color_mask[i]; 1048 auto& dest = state.color_mask[i];
1026 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE; 1049 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
@@ -1030,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() {
1030 } 1053 }
1031} 1054}
1032 1055
1056void RasterizerOpenGL::SyncMultiSampleState() {
1057 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1058 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
1059 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
1060}
1061
1062void RasterizerOpenGL::SyncFragmentColorClampState() {
1063 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1064 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
1065}
1066
1033void RasterizerOpenGL::SyncBlendState() { 1067void RasterizerOpenGL::SyncBlendState() {
1034 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1068 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1035 1069
@@ -1041,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() {
1041 state.independant_blend.enabled = regs.independent_blend_enable; 1075 state.independant_blend.enabled = regs.independent_blend_enable;
1042 if (!state.independant_blend.enabled) { 1076 if (!state.independant_blend.enabled) {
1043 auto& blend = state.blend[0]; 1077 auto& blend = state.blend[0];
1044 blend.enabled = regs.blend.enable[0] != 0; 1078 const auto& src = regs.blend;
1045 blend.separate_alpha = regs.blend.separate_alpha; 1079 blend.enabled = src.enable[0] != 0;
1046 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb); 1080 if (blend.enabled) {
1047 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb); 1081 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1048 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb); 1082 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1049 if (blend.separate_alpha) { 1083 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1050 blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a); 1084 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1051 blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a); 1085 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1052 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a); 1086 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1053 } 1087 }
1054 for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1088 for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1055 state.blend[i].enabled = false; 1089 state.blend[i].enabled = false;
1056 } 1090 }
1057 return; 1091 return;
1058 } 1092 }
1059 1093
1060 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1094 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1061 auto& blend = state.blend[i]; 1095 auto& blend = state.blend[i];
1096 const auto& src = regs.independent_blend[i];
1062 blend.enabled = regs.blend.enable[i] != 0; 1097 blend.enabled = regs.blend.enable[i] != 0;
1063 if (!blend.enabled) 1098 if (!blend.enabled)
1064 continue; 1099 continue;
1065 blend.separate_alpha = regs.independent_blend[i].separate_alpha; 1100 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1066 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb); 1101 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1067 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb); 1102 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1068 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb); 1103 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1069 if (blend.separate_alpha) { 1104 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1070 blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a); 1105 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1071 blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
1072 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
1073 }
1074 } 1106 }
1075} 1107}
1076 1108
1077void RasterizerOpenGL::SyncLogicOpState() { 1109void RasterizerOpenGL::SyncLogicOpState() {
1078 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1110 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1079 1111
1080 // TODO(Subv): Support more than just render target 0.
1081 state.logic_op.enabled = regs.logic_op.enable != 0; 1112 state.logic_op.enabled = regs.logic_op.enable != 0;
1082 1113
1083 if (!state.logic_op.enabled) 1114 if (!state.logic_op.enabled)
@@ -1090,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
1090} 1121}
1091 1122
1092void RasterizerOpenGL::SyncScissorTest() { 1123void RasterizerOpenGL::SyncScissorTest() {
1093 // TODO: what is the correct behavior here, a single scissor for all targets
1094 // or scissor disabled for the rest of the targets?
1095 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1124 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1096 state.scissor.enabled = (regs.scissor_test.enable != 0); 1125 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
1097 if (regs.scissor_test.enable == 0) { 1126 const auto& src = regs.scissor_test[i];
1098 return; 1127 auto& dst = state.viewports[i].scissor;
1128 dst.enabled = (src.enable != 0);
1129 if (dst.enabled == 0) {
1130 return;
1131 }
1132 const u32 width = src.max_x - src.min_x;
1133 const u32 height = src.max_y - src.min_y;
1134 dst.x = src.min_x;
1135 dst.y = src.min_y;
1136 dst.width = width;
1137 dst.height = height;
1099 } 1138 }
1100 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1101 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1102 state.scissor.x = regs.scissor_test.min_x;
1103 state.scissor.y = regs.scissor_test.min_y;
1104 state.scissor.width = width;
1105 state.scissor.height = height;
1106} 1139}
1107 1140
1108void RasterizerOpenGL::SyncTransformFeedback() { 1141void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1116,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1116 1149
1117void RasterizerOpenGL::SyncPointState() { 1150void RasterizerOpenGL::SyncPointState() {
1118 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1151 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1119 1152 state.point.size = regs.point_size;
1120 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
1121 // register carrying a default value. For now, if the point size is zero, assume it's
1122 // OpenGL's default (1).
1123 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1124} 1153}
1125 1154
1126void RasterizerOpenGL::CheckAlphaTests() { 1155void RasterizerOpenGL::CheckAlphaTests() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 8ef0f6c12..6e78ab4cd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -88,7 +88,7 @@ private:
88 /// SamplerInfo struct. 88 /// SamplerInfo struct.
89 void Create(); 89 void Create();
90 /// Syncs the sampler object with the config, updating any necessary state. 90 /// Syncs the sampler object with the config, updating any necessary state.
91 void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info); 91 void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
92 92
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter;
@@ -100,6 +100,10 @@ private:
100 bool uses_depth_compare; 100 bool uses_depth_compare;
101 Tegra::Texture::DepthCompareFunc depth_compare_func; 101 Tegra::Texture::DepthCompareFunc depth_compare_func;
102 GLvec4 border_color; 102 GLvec4 border_color;
103 float min_lod;
104 float max_lod;
105 float lod_bias;
106 float max_anisotropic;
103 }; 107 };
104 108
105 /** 109 /**
@@ -160,6 +164,12 @@ private:
160 /// Syncs the LogicOp state to match the guest state 164 /// Syncs the LogicOp state to match the guest state
161 void SyncLogicOpState(); 165 void SyncLogicOpState();
162 166
167 /// Syncs the the color clamp state
168 void SyncFragmentColorClampState();
169
170 /// Syncs the alpha coverage and alpha to one
171 void SyncMultiSampleState();
172
163 /// Syncs the scissor test state to match the guest state 173 /// Syncs the scissor test state to match the guest state
164 void SyncScissorTest(); 174 void SyncScissorTest();
165 175
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 2a069cdd8..9a5d7e289 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -67,6 +67,7 @@ public:
67 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); 67 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
68 state.draw.shader_program = 0; 68 state.draw.shader_program = 0;
69 state.draw.program_pipeline = pipeline.handle; 69 state.draw.program_pipeline = pipeline.handle;
70 state.geometry_shaders.enabled = (gs != 0);
70 } 71 }
71 72
72private: 73private:
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 98622a058..d9910c6e8 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
14bool OpenGLState::s_rgb_used; 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 geometry_shaders.enabled = false;
17 framebuffer_srgb.enabled = false; 18 framebuffer_srgb.enabled = false;
19 multisample_control.alpha_to_coverage = false;
20 multisample_control.alpha_to_one = false;
18 cull.enabled = false; 21 cull.enabled = false;
19 cull.mode = GL_BACK; 22 cull.mode = GL_BACK;
20 cull.front_face = GL_CCW; 23 cull.front_face = GL_CCW;
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
50 item.height = 0; 53 item.height = 0;
51 item.depth_range_near = 0.0f; 54 item.depth_range_near = 0.0f;
52 item.depth_range_far = 1.0f; 55 item.depth_range_far = 1.0f;
56 item.scissor.enabled = false;
57 item.scissor.x = 0;
58 item.scissor.y = 0;
59 item.scissor.width = 0;
60 item.scissor.height = 0;
53 } 61 }
54 scissor.enabled = false;
55 scissor.x = 0;
56 scissor.y = 0;
57 scissor.width = 0;
58 scissor.height = 0;
59 for (auto& item : blend) { 62 for (auto& item : blend) {
60 item.enabled = true; 63 item.enabled = true;
61 item.rgb_equation = GL_FUNC_ADD; 64 item.rgb_equation = GL_FUNC_ADD;
@@ -88,6 +91,7 @@ OpenGLState::OpenGLState() {
88 clip_distance = {}; 91 clip_distance = {};
89 92
90 point.size = 1; 93 point.size = 1;
94 fragment_color_clamp.enabled = false;
91} 95}
92 96
93void OpenGLState::ApplyDefaultState() { 97void OpenGLState::ApplyDefaultState() {
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
136} 140}
137 141
138void OpenGLState::ApplyColorMask() const { 142void OpenGLState::ApplyColorMask() const {
139 if (GLAD_GL_ARB_viewport_array) { 143 if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
140 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 144 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
141 const auto& updated = color_mask[i]; 145 const auto& updated = color_mask[i];
142 const auto& current = cur_state.color_mask[i]; 146 const auto& current = cur_state.color_mask[i];
@@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const {
230 } 234 }
231} 235}
232 236
233void OpenGLState::ApplyScissor() const {
234 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
235 if (scissor_changed) {
236 if (scissor.enabled) {
237 glEnable(GL_SCISSOR_TEST);
238 } else {
239 glDisable(GL_SCISSOR_TEST);
240 }
241 }
242 if (scissor.enabled &&
243 (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
244 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
245 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
246 }
247}
248
249void OpenGLState::ApplyViewport() const { 237void OpenGLState::ApplyViewport() const {
250 if (GLAD_GL_ARB_viewport_array) { 238 if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
251 for (GLuint i = 0; 239 for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
252 i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) { 240 i++) {
253 const auto& current = cur_state.viewports[i]; 241 const auto& current = cur_state.viewports[i];
254 const auto& updated = viewports[i]; 242 const auto& updated = viewports[i];
255 if (updated.x != current.x || updated.y != current.y || 243 if (updated.x != current.x || updated.y != current.y ||
@@ -260,6 +248,22 @@ void OpenGLState::ApplyViewport() const {
260 updated.depth_range_far != current.depth_range_far) { 248 updated.depth_range_far != current.depth_range_far) {
261 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); 249 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
262 } 250 }
251 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
252 if (scissor_changed) {
253 if (updated.scissor.enabled) {
254 glEnablei(GL_SCISSOR_TEST, i);
255 } else {
256 glDisablei(GL_SCISSOR_TEST, i);
257 }
258 }
259 if (updated.scissor.enabled &&
260 (scissor_changed || updated.scissor.x != current.scissor.x ||
261 updated.scissor.y != current.scissor.y ||
262 updated.scissor.width != current.scissor.width ||
263 updated.scissor.height != current.scissor.height)) {
264 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
265 updated.scissor.height);
266 }
263 } 267 }
264 } else { 268 } else {
265 const auto& current = cur_state.viewports[0]; 269 const auto& current = cur_state.viewports[0];
@@ -273,6 +277,21 @@ void OpenGLState::ApplyViewport() const {
273 updated.depth_range_far != current.depth_range_far) { 277 updated.depth_range_far != current.depth_range_far) {
274 glDepthRange(updated.depth_range_near, updated.depth_range_far); 278 glDepthRange(updated.depth_range_near, updated.depth_range_far);
275 } 279 }
280 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
281 if (scissor_changed) {
282 if (updated.scissor.enabled) {
283 glEnable(GL_SCISSOR_TEST);
284 } else {
285 glDisable(GL_SCISSOR_TEST);
286 }
287 }
288 if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
289 updated.scissor.y != current.scissor.y ||
290 updated.scissor.width != current.scissor.width ||
291 updated.scissor.height != current.scissor.height)) {
292 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
293 updated.scissor.height);
294 }
276 } 295 }
277} 296}
278 297
@@ -290,27 +309,16 @@ void OpenGLState::ApplyGlobalBlending() const {
290 if (!updated.enabled) { 309 if (!updated.enabled) {
291 return; 310 return;
292 } 311 }
293 if (updated.separate_alpha) { 312 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
294 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 313 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
295 updated.dst_rgb_func != current.dst_rgb_func || 314 updated.dst_a_func != current.dst_a_func) {
296 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { 315 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
297 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, 316 updated.dst_a_func);
298 updated.dst_a_func); 317 }
299 }
300
301 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
302 updated.a_equation != current.a_equation) {
303 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
304 }
305 } else {
306 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
307 updated.dst_rgb_func != current.dst_rgb_func) {
308 glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
309 }
310 318
311 if (blend_changed || updated.rgb_equation != current.rgb_equation) { 319 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
312 glBlendEquation(updated.rgb_equation); 320 updated.a_equation != current.a_equation) {
313 } 321 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
314 } 322 }
315} 323}
316 324
@@ -328,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
328 if (!updated.enabled) { 336 if (!updated.enabled) {
329 return; 337 return;
330 } 338 }
331 if (updated.separate_alpha) { 339 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
332 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 340 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
333 updated.dst_rgb_func != current.dst_rgb_func || 341 updated.dst_a_func != current.dst_a_func) {
334 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { 342 glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
335 glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func, 343 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
336 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); 344 }
337 }
338
339 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
340 updated.a_equation != current.a_equation) {
341 glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
342 updated.a_equation);
343 }
344 } else {
345 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
346 updated.dst_rgb_func != current.dst_rgb_func) {
347 glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
348 updated.dst_rgb_func);
349 }
350 345
351 if (blend_changed || updated.rgb_equation != current.rgb_equation) { 346 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
352 glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation); 347 updated.a_equation != current.a_equation) {
353 } 348 glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
349 updated.a_equation);
354 } 350 }
355} 351}
356 352
@@ -481,9 +477,29 @@ void OpenGLState::Apply() const {
481 if (point.size != cur_state.point.size) { 477 if (point.size != cur_state.point.size) {
482 glPointSize(point.size); 478 glPointSize(point.size);
483 } 479 }
480 if (GLAD_GL_ARB_color_buffer_float) {
481 if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
482 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
483 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
484 }
485 }
486 if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
487 if (multisample_control.alpha_to_coverage) {
488 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
489 } else {
490 glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
491 }
492 }
493 if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
494 if (multisample_control.alpha_to_one) {
495 glEnable(GL_SAMPLE_ALPHA_TO_ONE);
496 } else {
497 glDisable(GL_SAMPLE_ALPHA_TO_ONE);
498 }
499 }
500
484 ApplyColorMask(); 501 ApplyColorMask();
485 ApplyViewport(); 502 ApplyViewport();
486 ApplyScissor();
487 ApplyStencilTest(); 503 ApplyStencilTest();
488 ApplySRgb(); 504 ApplySRgb();
489 ApplyCulling(); 505 ApplyCulling();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index e5d1baae6..bdc743b0f 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -40,6 +40,19 @@ public:
40 } framebuffer_srgb; 40 } framebuffer_srgb;
41 41
42 struct { 42 struct {
43 bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
44 bool alpha_to_one; // GL_ALPHA_TO_ONE
45 } multisample_control;
46
47 struct {
48 bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
49 } fragment_color_clamp;
50
51 struct {
52 bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
53 } geometry_shaders;
54
55 struct {
43 bool enabled; // GL_CULL_FACE 56 bool enabled; // GL_CULL_FACE
44 GLenum mode; // GL_CULL_FACE_MODE 57 GLenum mode; // GL_CULL_FACE_MODE
45 GLenum front_face; // GL_FRONT_FACE 58 GLenum front_face; // GL_FRONT_FACE
@@ -79,7 +92,6 @@ public:
79 92
80 struct Blend { 93 struct Blend {
81 bool enabled; // GL_BLEND 94 bool enabled; // GL_BLEND
82 bool separate_alpha; // Independent blend enabled
83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB 95 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA 96 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
85 GLenum src_rgb_func; // GL_BLEND_SRC_RGB 97 GLenum src_rgb_func; // GL_BLEND_SRC_RGB
@@ -150,16 +162,15 @@ public:
150 GLfloat height; 162 GLfloat height;
151 GLfloat depth_range_near; // GL_DEPTH_RANGE 163 GLfloat depth_range_near; // GL_DEPTH_RANGE
152 GLfloat depth_range_far; // GL_DEPTH_RANGE 164 GLfloat depth_range_far; // GL_DEPTH_RANGE
165 struct {
166 bool enabled; // GL_SCISSOR_TEST
167 GLint x;
168 GLint y;
169 GLsizei width;
170 GLsizei height;
171 } scissor;
153 }; 172 };
154 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports; 173 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
155
156 struct {
157 bool enabled; // GL_SCISSOR_TEST
158 GLint x;
159 GLint y;
160 GLsizei width;
161 GLsizei height;
162 } scissor;
163 174
164 struct { 175 struct {
165 float size; // GL_POINT_SIZE 176 float size; // GL_POINT_SIZE
@@ -214,7 +225,6 @@ private:
214 void ApplyLogicOp() const; 225 void ApplyLogicOp() const;
215 void ApplyTextures() const; 226 void ApplyTextures() const;
216 void ApplySamplers() const; 227 void ApplySamplers() const;
217 void ApplyScissor() const;
218}; 228};
219 229
220} // namespace OpenGL 230} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 3ce2cc6d2..065b3929c 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -180,6 +180,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
180 return GL_CLAMP_TO_BORDER; 180 return GL_CLAMP_TO_BORDER;
181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
182 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
183 case Tegra::Texture::WrapMode::MirrorOnceBorder:
184 if (GL_EXT_texture_mirror_clamp) {
185 return GL_MIRROR_CLAMP_TO_BORDER_EXT;
186 } else {
187 return GL_MIRROR_CLAMP_TO_EDGE;
188 }
183 } 189 }
184 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 190 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
185 return GL_REPEAT; 191 return GL_REPEAT;
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index e199d019a..ffa08f5c1 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -190,6 +190,7 @@ struct TICEntry {
190 union { 190 union {
191 BitField<0, 4, u32> res_min_mip_level; 191 BitField<0, 4, u32> res_min_mip_level;
192 BitField<4, 4, u32> res_max_mip_level; 192 BitField<4, 4, u32> res_max_mip_level;
193 BitField<12, 12, u32> min_lod_clamp;
193 }; 194 };
194 195
195 GPUVAddr Address() const { 196 GPUVAddr Address() const {
@@ -284,13 +285,25 @@ struct TSCEntry {
284 BitField<6, 3, WrapMode> wrap_p; 285 BitField<6, 3, WrapMode> wrap_p;
285 BitField<9, 1, u32> depth_compare_enabled; 286 BitField<9, 1, u32> depth_compare_enabled;
286 BitField<10, 3, DepthCompareFunc> depth_compare_func; 287 BitField<10, 3, DepthCompareFunc> depth_compare_func;
288 BitField<13, 1, u32> srgb_conversion;
289 BitField<20, 3, u32> max_anisotropy;
287 }; 290 };
288 union { 291 union {
289 BitField<0, 2, TextureFilter> mag_filter; 292 BitField<0, 2, TextureFilter> mag_filter;
290 BitField<4, 2, TextureFilter> min_filter; 293 BitField<4, 2, TextureFilter> min_filter;
291 BitField<6, 2, TextureMipmapFilter> mip_filter; 294 BitField<6, 2, TextureMipmapFilter> mip_filter;
295 BitField<9, 1, u32> cubemap_interface_filtering;
296 BitField<12, 13, u32> mip_lod_bias;
297 };
298 union {
299 BitField<0, 12, u32> min_lod_clamp;
300 BitField<12, 12, u32> max_lod_clamp;
301 BitField<24, 8, u32> srgb_border_color_r;
302 };
303 union {
304 BitField<12, 8, u32> srgb_border_color_g;
305 BitField<20, 8, u32> srgb_border_color_b;
292 }; 306 };
293 INSERT_PADDING_BYTES(8);
294 float border_color_r; 307 float border_color_r;
295 float border_color_g; 308 float border_color_g;
296 float border_color_b; 309 float border_color_b;