diff options
| author | Ryan Houdek <Sonicadvance1@gmail.com> | 2014-11-28 11:45:38 -0600 |
|---|---|---|
| committer | Ryan Houdek <Sonicadvance1@gmail.com> | 2014-11-28 11:45:38 -0600 |
| commit | ce059769f6bf13118366f77fea926727efcf4510 (patch) | |
| tree | 8ea7f2014f086f1df0f661697863c5b72d633a5b /Source/Core/VideoCommon | |
| parent | 0e3d20c349ead263a630463beeb4335aaf512e42 (diff) | |
| parent | 6d51455195e95a1bb97eb263fcd3423c757f59e7 (diff) | |
Merge pull request #1439 from Armada651/ogl-stereo-3d
OGL: Stereoscopic 3D Support
Diffstat (limited to 'Source/Core/VideoCommon')
22 files changed, 410 insertions, 93 deletions
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 87b23a25ff..0dfd12b732 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -9,6 +9,7 @@ set(SRCS BoundingBox.cpp Fifo.cpp FPSCounter.cpp FramebufferManagerBase.cpp + GeometryShaderGen.cpp HiresTextures.cpp ImageWrite.cpp IndexGenerator.cpp diff --git a/Source/Core/VideoCommon/ConstantManager.h b/Source/Core/VideoCommon/ConstantManager.h index 3e52640ca9..116088d76e 100644 --- a/Source/Core/VideoCommon/ConstantManager.h +++ b/Source/Core/VideoCommon/ConstantManager.h @@ -43,5 +43,6 @@ struct VertexShaderConstants float4 normalmatrices[32]; float4 posttransformmatrices[64]; float4 depthparams; + float4 stereoparams; }; diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.cpp b/Source/Core/VideoCommon/FramebufferManagerBase.cpp index ea865cd275..e2a09c6527 100644 --- a/Source/Core/VideoCommon/FramebufferManagerBase.cpp +++ b/Source/Core/VideoCommon/FramebufferManagerBase.cpp @@ -12,6 +12,8 @@ const XFBSourceBase* FramebufferManagerBase::m_overlappingXFBArray[MAX_VIRTUAL_X unsigned int FramebufferManagerBase::s_last_xfb_width = 1; unsigned int FramebufferManagerBase::s_last_xfb_height = 1; +unsigned int FramebufferManagerBase::m_EFBLayers = 1; + FramebufferManagerBase::FramebufferManagerBase() { m_realXFBSource = nullptr; diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.h b/Source/Core/VideoCommon/FramebufferManagerBase.h index 50813539ab..b01e820243 100644 --- a/Source/Core/VideoCommon/FramebufferManagerBase.h +++ b/Source/Core/VideoCommon/FramebufferManagerBase.h @@ -55,6 +55,8 @@ public: static int ScaleToVirtualXfbWidth(int x, unsigned int backbuffer_width); static int ScaleToVirtualXfbHeight(int y, unsigned int backbuffer_height); + static int GetEFBLayers() { return m_EFBLayers; } + protected: struct VirtualXFB { @@ -70,6 +72,8 @@ protected: typedef std::list<VirtualXFB> VirtualXFBListType; + static unsigned int m_EFBLayers; + private: virtual XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height) = 0; // TODO: figure out why OGL is different for this guy diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp new file mode 100644 index 0000000000..83a590a7ed --- /dev/null +++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp @@ -0,0 +1,134 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include <cmath> +#include <locale.h> +#ifdef __APPLE__ + #include <xlocale.h> +#endif + +#include "VideoCommon/GeometryShaderGen.h" +#include "VideoCommon/LightingShaderGen.h" +#include "VideoCommon/VertexShaderGen.h" +#include "VideoCommon/VideoConfig.h" + +static char text[16384]; + +template<class T> +static inline void GenerateGeometryShader(T& out, u32 components, API_TYPE ApiType) +{ + // Non-uid template parameters will write to the dummy data (=> gets optimized out) + geometry_shader_uid_data dummy_data; + geometry_shader_uid_data* uid_data = out.template GetUidData<geometry_shader_uid_data>(); + if (uid_data == nullptr) + uid_data = &dummy_data; + + out.SetBuffer(text); + const bool is_writing_shadercode = (out.GetBuffer() != nullptr); +#ifndef ANDROID + locale_t locale; + locale_t old_locale; + if (is_writing_shadercode) + { + locale = newlocale(LC_NUMERIC_MASK, "C", nullptr); // New locale for compilation + old_locale = uselocale(locale); // Apply the locale for this thread + } +#endif + + if (is_writing_shadercode) + text[sizeof(text) - 1] = 0x7C; // canary + + out.Write("//Geometry Shader for 3D stereoscopy\n"); + + uid_data->stereo = g_ActiveConfig.iStereoMode > 0; + if (ApiType == API_OPENGL) + { + // Insert layout parameters + if (g_ActiveConfig.backend_info.bSupportsGSInstancing) + out.Write("layout(triangles, invocations = %d) in;\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1); + else + out.Write("layout(triangles) in;\n"); + out.Write("layout(triangle_strip, max_vertices = %d) out;\n", g_ActiveConfig.backend_info.bSupportsGSInstancing ? 3 : 6); + } + + out.Write("%s", s_lighting_struct); + + // uniforms + if (ApiType == API_OPENGL) + out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : ""); + else + out.Write("cbuffer VSBlock {\n"); + out.Write(s_shader_uniforms); + out.Write("};\n"); + + uid_data->numTexGens = xfmem.numTexGen.numTexGens; + uid_data->pixel_lighting = g_ActiveConfig.bEnablePixelLighting; + + GenerateVSOutputStruct(out, ApiType); + + out.Write("centroid in VS_OUTPUT o[3];\n"); + out.Write("centroid out VS_OUTPUT f;\n"); + + out.Write("flat out int layer;\n"); + + out.Write("void main()\n{\n"); + + // If the GPU supports invocation we don't need a for loop and can simply use the + // invocation identifier to determine which layer we're rendering. + if (g_ActiveConfig.backend_info.bSupportsGSInstancing) + out.Write("\tint l = gl_InvocationID;\n"); + else + out.Write("\tfor (int l = 0; l < %d; ++l) {\n", g_ActiveConfig.iStereoMode > 0 ? 2 : 1); + + out.Write("\tfor (int i = 0; i < 3; ++i) {\n"); + out.Write("\t\tlayer = l;\n"); + out.Write("\t\tgl_Layer = l;\n"); + out.Write("\t\tf = o[i];\n"); + out.Write("\t\tfloat4 pos = o[i].pos;\n"); + + if (g_ActiveConfig.iStereoMode > 0) + { + // For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional + // to the depth of the vertex. We retrieve the depth value from the w-component of the projected + // vertex which contains the negated z-component of the original vertex. + // For negative parallax (out-of-screen effects) we subtract a convergence value from + // the depth value. This results in objects at a distance smaller than the convergence + // distance to seemingly appear in front of the screen. + // This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide" + out.Write("\t\tf.clipPos.x = o[i].clipPos.x + " I_STEREOPARAMS"[l] * (o[i].clipPos.w - " I_STEREOPARAMS"[2]);\n"); + out.Write("\t\tpos.x = o[i].pos.x + " I_STEREOPARAMS"[l] * (o[i].pos.w - " I_STEREOPARAMS"[2]);\n"); + } + + out.Write("\t\tf.pos.x = pos.x;\n"); + out.Write("\t\tgl_Position = pos;\n"); + out.Write("\t\tEmitVertex();\n"); + out.Write("\t}\n"); + out.Write("\tEndPrimitive();\n"); + + if (!g_ActiveConfig.backend_info.bSupportsGSInstancing) + out.Write("\t}\n"); + + out.Write("}\n"); + + if (is_writing_shadercode) + { + if (text[sizeof(text) - 1] != 0x7C) + PanicAlert("GeometryShader generator - buffer too small, canary has been eaten!"); + +#ifndef ANDROID + uselocale(old_locale); // restore locale + freelocale(locale); +#endif + } +} + +void GetGeometryShaderUid(GeometryShaderUid& object, u32 components, API_TYPE ApiType) +{ + GenerateGeometryShader<GeometryShaderUid>(object, components, ApiType); +} + +void GenerateGeometryShaderCode(ShaderCode& object, u32 components, API_TYPE ApiType) +{ + GenerateGeometryShader<ShaderCode>(object, components, ApiType); +} diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h new file mode 100644 index 0000000000..3f7dc5198b --- /dev/null +++ b/Source/Core/VideoCommon/GeometryShaderGen.h @@ -0,0 +1,26 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "VideoCommon/ShaderGenCommon.h" +#include "VideoCommon/VideoCommon.h" + +#pragma pack(1) + +struct geometry_shader_uid_data +{ + u32 NumValues() const { return sizeof(geometry_shader_uid_data); } + + u32 stereo : 1; + u32 numTexGens : 4; + u32 pixel_lighting : 1; +}; + +#pragma pack() + +typedef ShaderUid<geometry_shader_uid_data> GeometryShaderUid; + +void GenerateGeometryShaderCode(ShaderCode& object, u32 components, API_TYPE ApiType); +void GetGeometryShaderUid(GeometryShaderUid& object, u32 components, API_TYPE ApiType); diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index c0b45fdfda..71e534df0a 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -16,6 +16,7 @@ #include "VideoCommon/LightingShaderGen.h" #include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/PixelShaderGen.h" +#include "VideoCommon/VertexShaderGen.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" // for texture projection mode @@ -206,7 +207,7 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T { // Declare samplers for (int i = 0; i < 8; ++i) - out.Write("SAMPLER_BINDING(%d) uniform sampler2D samp%d;\n", i, i); + out.Write("SAMPLER_BINDING(%d) uniform sampler2DArray samp%d;\n", i, i); } else // D3D { @@ -253,17 +254,8 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T { out.Write("cbuffer VSBlock : register(b1) {\n"); } - out.Write( - "\tfloat4 " I_POSNORMALMATRIX"[6];\n" - "\tfloat4 " I_PROJECTION"[4];\n" - "\tint4 " I_MATERIALS"[4];\n" - "\tLight " I_LIGHTS"[8];\n" - "\tfloat4 " I_TEXMATRICES"[24];\n" - "\tfloat4 " I_TRANSFORMMATRICES"[64];\n" - "\tfloat4 " I_NORMALMATRICES"[32];\n" - "\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n" - "\tfloat4 " I_DEPTHPARAMS";\n" - "};\n"); + out.Write(s_shader_uniforms); + out.Write("};\n"); } if (g_ActiveConfig.backend_info.bSupportsBBox) @@ -275,6 +267,8 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T ); } + GenerateVSOutputStruct(out, ApiType); + const bool forced_early_z = g_ActiveConfig.backend_info.bSupportsEarlyZ && bpmem.UseEarlyDepthTest() && (g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED); const bool per_pixel_depth = (bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) || (!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z); @@ -325,22 +319,52 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T // As a workaround, we interpolate at the centroid of the coveraged pixel, which // is always inside the primitive. // Without MSAA, this flag is defined to have no effect. - out.Write("centroid in float4 colors_02;\n"); - out.Write("centroid in float4 colors_12;\n"); - - // compute window position if needed because binding semantic WPOS is not widely supported - // Let's set up attributes - for (unsigned int i = 0; i < numTexgen; ++i) + uid_data->stereo = g_ActiveConfig.iStereoMode > 0; + if (g_ActiveConfig.iStereoMode > 0) { - out.Write("centroid in float3 uv%d;\n", i); + out.Write("centroid in VS_OUTPUT f;\n"); + out.Write("flat in int layer;\n"); } - out.Write("centroid in float4 clipPos;\n"); - if (g_ActiveConfig.bEnablePixelLighting) + else { - out.Write("centroid in float4 Normal;\n"); + out.Write("centroid in float4 colors_02;\n"); + out.Write("centroid in float4 colors_12;\n"); + + // compute window position if needed because binding semantic WPOS is not widely supported + // Let's set up attributes + for (unsigned int i = 0; i < numTexgen; ++i) + { + out.Write("centroid in float3 uv%d;\n", i); + } + out.Write("centroid in float4 clipPos;\n"); + if (g_ActiveConfig.bEnablePixelLighting) + { + out.Write("centroid in float4 Normal;\n"); + } } out.Write("void main()\n{\n"); + + if (g_ActiveConfig.iStereoMode > 0) + { + // compute window position if needed because binding semantic WPOS is not widely supported + // Let's set up attributes + for (unsigned int i = 0; i < numTexgen; ++i) + { + out.Write("\tfloat3 uv%d = f.tex%d;\n", i, i); + } + out.Write("\tfloat4 clipPos = f.clipPos;\n"); + if (g_ActiveConfig.bEnablePixelLighting) + { + out.Write("\tfloat4 Normal = f.Normal;\n"); + } + } + + // On Mali, global variables must be initialized as constants. + // This is why we initialize these variables locally instead. + out.Write("\tfloat4 colors_0 = %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "f.colors_0" : "colors_02"); + out.Write("\tfloat4 colors_1 = %s;\n", (g_ActiveConfig.iStereoMode > 0) ? "f.colors_1" : "colors_12"); + out.Write("\tfloat4 rawpos = gl_FragCoord;\n"); } else // D3D @@ -370,14 +394,6 @@ static inline void GeneratePixelShader(T& out, DSTALPHA_MODE dstAlphaMode, API_T "\tint2 wrappedcoord=int2(0,0), tempcoord=int2(0,0);\n" "\tint4 tevin_a=int4(0,0,0,0),tevin_b=int4(0,0,0,0),tevin_c=int4(0,0,0,0),tevin_d=int4(0,0,0,0);\n\n"); // tev combiner inputs - if (ApiType == API_OPENGL) - { - // On Mali, global variables must be initialized as constants. - // This is why we initialize these variables locally instead. - out.Write("\tfloat4 colors_0 = colors_02;\n"); - out.Write("\tfloat4 colors_1 = colors_12;\n"); - } - if (g_ActiveConfig.bEnablePixelLighting) { out.Write("\tfloat3 _norm0 = normalize(Normal.xyz);\n\n"); @@ -931,7 +947,7 @@ static inline void SampleTexture(T& out, const char *texcoords, const char *texs if (ApiType == API_D3D) out.Write("iround(255.0 * Tex%d.Sample(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap,texmap, texcoords, texmap, texswap); else - out.Write("iround(255.0 * texture(samp%d,%s.xy * " I_TEXDIMS"[%d].xy)).%s;\n", texmap, texcoords, texmap, texswap); + out.Write("iround(255.0 * texture(samp%d, float3(%s.xy * " I_TEXDIMS"[%d].xy, %s))).%s;\n", texmap, texcoords, texmap, g_ActiveConfig.iStereoMode > 0 ? "layer" : "0.0", texswap); } static const char *tevAlphaFuncsTable[] = diff --git a/Source/Core/VideoCommon/PixelShaderGen.h b/Source/Core/VideoCommon/PixelShaderGen.h index 369f940619..784523087a 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.h +++ b/Source/Core/VideoCommon/PixelShaderGen.h @@ -44,7 +44,7 @@ struct pixel_shader_uid_data u32 dstAlphaMode : 2; u32 Pretest : 2; u32 nIndirectStagesUsed : 4; - u32 pad0 : 1; + u32 stereo : 1; u32 genMode_numtexgens : 4; u32 genMode_numtevstages : 4; diff --git a/Source/Core/VideoCommon/PostProcessing.h b/Source/Core/VideoCommon/PostProcessing.h index 68ab47cff6..7c4f942214 100644 --- a/Source/Core/VideoCommon/PostProcessing.h +++ b/Source/Core/VideoCommon/PostProcessing.h @@ -91,7 +91,7 @@ public: // Should be implemented by the backends for backend specific code virtual void BlitFromTexture(TargetRectangle src, TargetRectangle dst, - int src_texture, int src_width, int src_height) = 0; + int src_texture, int src_width, int src_height, int layer = 0) = 0; virtual void ApplyShader() = 0; protected: diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index d93d43f490..84c037c753 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -240,6 +240,42 @@ bool Renderer::CalculateTargetSize(unsigned int framebuffer_width, unsigned int return false; } +void Renderer::ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc, TargetRectangle& rightRc) +{ + // Resize target to half its original size + TargetRectangle drawRc = rc; + if (g_ActiveConfig.iStereoMode == STEREO_TAB) + { + // The height may be negative due to flipped rectangles + int height = rc.bottom - rc.top; + drawRc.top += height / 4; + drawRc.bottom -= height / 4; + } + else + { + int width = rc.right - rc.left; + drawRc.left += width / 4; + drawRc.right -= width / 4; + } + + // Create two target rectangle offset to the sides of the backbuffer + leftRc = drawRc, rightRc = drawRc; + if (g_ActiveConfig.iStereoMode == STEREO_TAB) + { + leftRc.top -= s_backbuffer_height / 4; + leftRc.bottom -= s_backbuffer_height / 4; + rightRc.top += s_backbuffer_height / 4; + rightRc.bottom += s_backbuffer_height / 4; + } + else + { + leftRc.left -= s_backbuffer_width / 4; + leftRc.right -= s_backbuffer_width / 4; + rightRc.left += s_backbuffer_width / 4; + rightRc.right += s_backbuffer_width / 4; + } +} + void Renderer::SetScreenshot(const std::string& filename) { std::lock_guard<std::mutex> lk(s_criticalScreenshot); diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 746f75366c..f59458e523 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -83,6 +83,8 @@ public: static const TargetRectangle& GetTargetRectangle() { return target_rc; } static void UpdateDrawRectangle(int backbuffer_width, int backbuffer_height); + // Use this to convert a single target rectangle to two stereo rectangles + static void ConvertStereoRectangle(const TargetRectangle& rc, TargetRectangle& leftRc, TargetRectangle& rightRc); // Use this to upscale native EFB coordinates to IDEAL internal resolution static int EFBToScaledX(int x); diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h index 54adb1ba17..330b9f8ced 100644 --- a/Source/Core/VideoCommon/ShaderGenCommon.h +++ b/Source/Core/VideoCommon/ShaderGenCommon.h @@ -239,3 +239,16 @@ private: #define I_NORMALMATRICES "cnmtx" #define I_POSTTRANSFORMMATRICES "cpostmtx" #define I_DEPTHPARAMS "cDepth" // farZ, zRange +#define I_STEREOPARAMS "cstereo" + +static const char s_shader_uniforms[] = + "\tfloat4 " I_POSNORMALMATRIX"[6];\n" + "\tfloat4 " I_PROJECTION"[4];\n" + "\tint4 " I_MATERIALS"[4];\n" + "\tLight " I_LIGHTS"[8];\n" + "\tfloat4 " I_TEXMATRICES"[24];\n" + "\tfloat4 " I_TRANSFORMMATRICES"[64];\n" + "\tfloat4 " I_NORMALMATRICES"[32];\n" + "\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n" + "\tfloat4 " I_DEPTHPARAMS";\n" + "\tfloat4 " I_STEREOPARAMS";\n"; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 0618b3f1b9..7fa540022d 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -115,6 +115,13 @@ void TextureCache::OnConfigChanged(VideoConfig& config) { g_texture_cache->ClearRenderTargets(); } + + if ((config.iStereoMode > 0) != backup_config.s_stereo_3d || + config.bStereoMonoEFBDepth != backup_config.s_mono_efb_depth) + { + g_texture_cache->DeleteShaders(); + g_texture_cache->CompileShaders(); + } } backup_config.s_colorsamples = config.iSafeTextureCache_ColorSamples; @@ -126,6 +133,8 @@ void TextureCache::OnConfigChanged(VideoConfig& config) backup_config.s_texfmt_overlay_center = config.bTexFmtOverlayCenter; backup_config.s_hires_textures = config.bHiresTextures; backup_config.s_copy_cache_enable = config.bEFBCopyCacheEnable; + backup_config.s_stereo_3d = config.iStereoMode > 0; + backup_config.s_mono_efb_depth = config.bStereoMonoEFBDepth; } void TextureCache::Cleanup() @@ -444,14 +453,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, // // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? // TODO: Actually, it should be enough if the internal texture format matches... - if ((entry->type == TCET_NORMAL && + if (((entry->type == TCET_NORMAL && width == entry->virtual_width && height == entry->virtual_height && full_format == entry->format && entry->num_mipmaps > maxlevel) || (entry->type == TCET_EC_DYNAMIC && entry->native_width == width && - entry->native_height == height)) + entry->native_height == height)) && + entry->num_layers == 1) { // reuse the texture } @@ -519,6 +529,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, // But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after. // Currently, we might try to reuse a texture which appears to have more levels than actual, maybe.. entry->num_mipmaps = maxlevel + 1; + entry->num_layers = 1; entry->type = TCET_NORMAL; GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); @@ -529,7 +540,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, entry->Load(width, height, expandedWidth, 0); } - entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); + entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps, entry->num_layers); entry->SetDimensions(nativeW, nativeH, width, height); entry->hash = tex_hash; @@ -886,12 +897,12 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat TCacheEntryBase *entry = textures[dstAddr]; if (entry) { - if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h) + if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h && entry->num_layers == FramebufferManagerBase::GetEFBLayers()) { scaled_tex_w = tex_w; scaled_tex_h = tex_h; } - else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h)) + else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h && entry->num_layers == FramebufferManagerBase::GetEFBLayers())) { if (entry->type == TCET_EC_VRAM) { @@ -914,7 +925,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat textures[dstAddr] = entry = AllocateRenderTarget(scaled_tex_w, scaled_tex_h); // TODO: Using the wrong dstFormat, dumb... - entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1); + entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1, FramebufferManagerBase::GetEFBLayers()); entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); entry->SetHashes(TEXHASH_INVALID); entry->type = TCET_EC_VRAM; diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index fbe13915fa..d9cd2b8c16 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -39,6 +39,7 @@ public: enum TexCacheEntryType type; unsigned int num_mipmaps; + unsigned int num_layers; unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies @@ -46,12 +47,13 @@ public: int frameCount; - void SetGeneralParameters(u32 _addr, u32 _size, u32 _format, unsigned int _num_mipmaps) + void SetGeneralParameters(u32 _addr, u32 _size, u32 _format, unsigned int _num_mipmaps, unsigned int _num_layers) { addr = _addr; size_in_bytes = _size; format = _format; num_mipmaps = _num_mipmaps; + num_layers = _num_layers; } void SetDimensions(unsigned int _native_width, unsigned int _native_height, unsigned int _virtual_width, unsigned int _virtual_height) @@ -101,6 +103,9 @@ public: unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0; virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0; + virtual void CompileShaders() = 0; // currently only implemented by OGL + virtual void DeleteShaders() = 0; // currently only implemented by OGL + static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, int format, unsigned int tlutaddr, int tlutfmt, bool use_mipmaps, unsigned int maxlevel, bool from_tmem); static void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, PEControl::PixelFormat srcFormat, @@ -140,6 +145,8 @@ private: bool s_texfmt_overlay_center; bool s_hires_textures; bool s_copy_cache_enable; + bool s_stereo_3d; + bool s_mono_efb_depth; } backup_config; }; diff --git a/Source/Core/VideoCommon/TextureConversionShader.cpp b/Source/Core/VideoCommon/TextureConversionShader.cpp index b0215e4c08..1ab973d11b 100644 --- a/Source/Core/VideoCommon/TextureConversionShader.cpp +++ b/Source/Core/VideoCommon/TextureConversionShader.cpp @@ -70,7 +70,7 @@ static void WriteSwizzler(char*& p, u32 format, API_TYPE ApiType) if (ApiType == API_OPENGL) { WRITE(p, "#define samp0 samp9\n"); - WRITE(p, "SAMPLER_BINDING(9) uniform sampler2D samp0;\n"); + WRITE(p, "SAMPLER_BINDING(9) uniform sampler2DArray samp0;\n"); WRITE(p, " out vec4 ocol0;\n"); WRITE(p, "void main()\n"); @@ -120,7 +120,7 @@ static void WriteSwizzler(char*& p, u32 format, API_TYPE ApiType) static void WriteSampleColor(char*& p, const char* colorComp, const char* dest, int xoffset, API_TYPE ApiType) { - WRITE(p, " %s = texture(samp0, uv0 + float2(%d, 0) * sample_offset).%s;\n", + WRITE(p, " %s = texture(samp0, float3(uv0 + float2(%d, 0) * sample_offset, 0.0)).%s;\n", dest, xoffset, colorComp ); } diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp index 894a18a0e2..24c6f39f46 100644 --- a/Source/Core/VideoCommon/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/VertexShaderGen.cpp @@ -89,17 +89,8 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ out.Write("layout(std140%s) uniform VSBlock {\n", g_ActiveConfig.backend_info.bSupportsBindingLayout ? ", binding = 2" : ""); else out.Write("cbuffer VSBlock {\n"); - out.Write( - "\tfloat4 " I_POSNORMALMATRIX"[6];\n" - "\tfloat4 " I_PROJECTION"[4];\n" - "\tint4 " I_MATERIALS"[4];\n" - "\tLight " I_LIGHTS"[8];\n" - "\tfloat4 " I_TEXMATRICES"[24];\n" - "\tfloat4 " I_TRANSFORMMATRICES"[64];\n" - "\tfloat4 " I_NORMALMATRICES"[32];\n" - "\tfloat4 " I_POSTTRANSFORMMATRICES"[64];\n" - "\tfloat4 " I_DEPTHPARAMS";\n" - "};\n"); + out.Write(s_shader_uniforms); + out.Write("};\n"); GenerateVSOutputStruct(out, api_type); @@ -131,22 +122,33 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ out.Write("in float%d tex%d; // ATTR%d,\n", hastexmtx ? 3 : 2, i, SHADER_TEXTURE0_ATTRIB + i); } - // Let's set up attributes - for (size_t i = 0; i < 8; ++i) + uid_data->stereo = g_ActiveConfig.iStereoMode > 0; + if (g_ActiveConfig.iStereoMode > 0) { - if (i < xfmem.numTexGen.numTexGens) + out.Write("centroid out VS_OUTPUT o;\n"); + } + else + { + // Let's set up attributes + for (size_t i = 0; i < 8; ++i) { - out.Write("centroid out float3 uv%d;\n", i); + if (i < xfmem.numTexGen.numTexGens) + { + out.Write("centroid out float3 uv%d;\n", i); + } } - } - out.Write("centroid out float4 clipPos;\n"); - if (g_ActiveConfig.bEnablePixelLighting) - out.Write("centroid out float4 Normal;\n"); - out.Write("centroid out float4 colors_02;\n"); - out.Write("centroid out float4 colors_12;\n"); + out.Write("centroid out float4 clipPos;\n"); + if (g_ActiveConfig.bEnablePixelLighting) + out.Write("centroid out float4 Normal;\n"); + out.Write("centroid out float4 colors_02;\n"); + out.Write("centroid out float4 colors_12;\n"); + } out.Write("void main()\n{\n"); + + if (g_ActiveConfig.iStereoMode <= 0) + out.Write("VS_OUTPUT o;\n"); } else // D3D { @@ -172,8 +174,9 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ if (components & VB_HAS_POSMTXIDX) out.Write(" int posmtx : BLENDINDICES,\n"); out.Write(" float4 rawpos : POSITION) {\n"); + + out.Write("VS_OUTPUT o;\n"); } - out.Write("VS_OUTPUT o;\n"); // transforms if (components & VB_HAS_POSMTXIDX) @@ -431,27 +434,32 @@ static inline void GenerateVertexShader(T& out, u32 components, API_TYPE api_typ if (api_type == API_OPENGL) { - // Bit ugly here - // TODO: Make pretty - // Will look better when we bind uniforms in GLSL 1.3 - // clipPos/w needs to be done in pixel shader, not here + if (g_ActiveConfig.iStereoMode <= 0) + { + // Bit ugly here + // TODO: Make pretty + // Will look better when we bind uniforms in GLSL 1.3 + // clipPos/w needs to be done in pixel shader, not here + + for (unsigned int i = 0; i < xfmem.numTexGen.numTexGens; ++i) + out.Write("uv%d.xyz = o.tex%d;\n", i, i); - for (unsigned int i = 0; i < xfmem.numTexGen.numTexGens; ++i) - out.Write(" uv%d.xyz = o.tex%d;\n", i, i); - out.Write(" clipPos = o.clipPos;\n"); + out.Write("clipPos = o.clipPos;\n"); - if (g_ActiveConfig.bEnablePixelLighting) - out.Write(" Normal = o.Normal;\n"); + if (g_ActiveConfig.bEnablePixelLighting) + out.Write("Normal = o.Normal;\n"); + + out.Write("colors_02 = o.colors_0;\n"); + out.Write("colors_12 = o.colors_1;\n"); + } - out.Write("colors_02 = o.colors_0;\n"); - out.Write("colors_12 = o.colors_1;\n"); out.Write("gl_Position = o.pos;\n"); - out.Write("}\n"); } else // D3D { - out.Write("return o;\n}\n"); + out.Write("return o;\n"); } + out.Write("}\n"); if (is_writing_shadercode) { @@ -475,7 +483,12 @@ void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE GenerateVertexShader<VertexShaderCode>(object, components, api_type); } -void GenerateVSOutputStructForGS(ShaderCode& object, API_TYPE api_type) +void GenerateVSOutputStruct(ShaderCode& object, API_TYPE api_type) { GenerateVSOutputStruct<ShaderCode>(object, api_type); } + +void GenerateVSOutputStruct(ShaderGeneratorInterface& object, API_TYPE api_type) +{ + // Ignore unknown types +} diff --git a/Source/Core/VideoCommon/VertexShaderGen.h b/Source/Core/VideoCommon/VertexShaderGen.h index 998f8fe6bc..b5e9e33f29 100644 --- a/Source/Core/VideoCommon/VertexShaderGen.h +++ b/Source/Core/VideoCommon/VertexShaderGen.h @@ -38,7 +38,7 @@ struct vertex_shader_uid_data u32 numColorChans : 2; u32 dualTexTrans_enabled : 1; u32 pixel_lighting : 1; - u32 pad0 : 1; + u32 stereo : 1; u32 texMtxInfo_n_projection : 16; // Stored separately to guarantee that the texMtxInfo struct is 8 bits wide struct { @@ -64,4 +64,5 @@ typedef ShaderCode VertexShaderCode; // TODO: Obsolete.. void GetVertexShaderUid(VertexShaderUid& object, u32 components, API_TYPE api_type); void GenerateVertexShaderCode(VertexShaderCode& object, u32 components, API_TYPE api_type); -void GenerateVSOutputStructForGS(ShaderCode& object, API_TYPE api_type); +void GenerateVSOutputStruct(ShaderCode& object, API_TYPE api_type); +void GenerateVSOutputStruct(ShaderGeneratorInterface& object, API_TYPE api_type); diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 93f969b0b5..8de50c5436 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -489,7 +489,7 @@ void VertexShaderManager::SetConstants() PRIM_LOG("Projection: %f %f %f %f %f %f\n", rawProjection[0], rawProjection[1], rawProjection[2], rawProjection[3], rawProjection[4], rawProjection[5]); - if ((g_ActiveConfig.bFreeLook || g_ActiveConfig.bAnaglyphStereo ) && xfmem.projection.type == GX_PERSPECTIVE) + if (g_ActiveConfig.bFreeLook && xfmem.projection.type == GX_PERSPECTIVE) { Matrix44 mtxA; Matrix44 mtxB; @@ -512,6 +512,19 @@ void VertexShaderManager::SetConstants() Matrix44::Multiply(s_viewportCorrection, projMtx, correctedMtx); memcpy(constants.projection, correctedMtx.data, 4*16); } + + if (g_ActiveConfig.iStereoMode > 0 && xfmem.projection.type == GX_PERSPECTIVE) + { + float offset = (g_ActiveConfig.iStereoSeparation / 1000.0f) * (g_ActiveConfig.iStereoSeparationPercent / 100.0f); + constants.stereoparams[0] = (g_ActiveConfig.bStereoSwapEyes) ? offset : -offset; + constants.stereoparams[1] = (g_ActiveConfig.bStereoSwapEyes) ? -offset : offset; + constants.stereoparams[2] = (g_ActiveConfig.iStereoConvergence / 10.0f) * (g_ActiveConfig.iStereoConvergencePercent / 100.0f); + } + else + { + constants.stereoparams[0] = constants.stereoparams[1] = 0; + } + dirty = true; } } diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index bda73f396c..31dc089736 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -60,6 +60,7 @@ <ClCompile Include="PostProcessing.cpp" /> <ClCompile Include="RenderBase.cpp" /> <ClCompile Include="Statistics.cpp" /> + <ClCompile Include="GeometryShaderGen.cpp" /> <ClCompile Include="TextureCacheBase.cpp" /> <ClCompile Include="TextureConversionShader.cpp" /> <ClCompile Include="VertexLoader.cpp" /> @@ -110,6 +111,7 @@ <ClInclude Include="RenderBase.h" /> <ClInclude Include="ShaderGenCommon.h" /> <ClInclude Include="Statistics.h" /> + <ClInclude Include="GeometryShaderGen.h" /> <ClInclude Include="TextureCacheBase.h" /> <ClInclude Include="TextureConversionShader.h" /> <ClInclude Include="TextureDecoder.h" /> diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index c662422200..f285ce35d4 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -143,6 +143,9 @@ <ClCompile Include="BoundingBox.cpp"> <Filter>Util</Filter> </ClCompile> + <ClCompile Include="GeometryShaderGen.cpp"> + <Filter>Shader Generators</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="CommandProcessor.h" /> @@ -275,6 +278,9 @@ <ClInclude Include="BoundingBox.h"> <Filter>Util</Filter> </ClInclude> + <ClInclude Include="GeometryShaderGen.h"> + <Filter>Shader Generators</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Text Include="CMakeLists.txt" /> diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index df72df9be2..2b6025f13e 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -38,6 +38,11 @@ VideoConfig::VideoConfig() backend_info.APIType = API_NONE; backend_info.bUseMinimalMipCount = false; backend_info.bSupportsExclusiveFullscreen = false; + + // Game-specific stereoscopy settings + bStereoMonoEFBDepth = false; + iStereoSeparationPercent = 100; + iStereoConvergencePercent = 100; } void VideoConfig::Load(const std::string& ini_file) @@ -66,9 +71,6 @@ void VideoConfig::Load(const std::string& ini_file) settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0); settings->Get("FreeLook", &bFreeLook, 0); settings->Get("UseFFV1", &bUseFFV1, 0); - settings->Get("AnaglyphStereo", &bAnaglyphStereo, false); - settings->Get("AnaglyphStereoSeparation", &iAnaglyphStereoSeparation, 200); - settings->Get("AnaglyphFocalAngle", &iAnaglyphFocalAngle, 0); settings->Get("EnablePixelLighting", &bEnablePixelLighting, 0); settings->Get("FastDepthCalc", &bFastDepthCalc, true); settings->Get("MSAA", &iMultisampleMode, 0); @@ -85,6 +87,10 @@ void VideoConfig::Load(const std::string& ini_file) enhancements->Get("ForceFiltering", &bForceFiltering, 0); enhancements->Get("MaxAnisotropy", &iMaxAnisotropy, 0); // NOTE - this is x in (1 << x) enhancements->Get("PostProcessingShader", &sPostProcessingShader, ""); + enhancements->Get("StereoMode", &iStereoMode, 0); + enhancements->Get("StereoSeparation", &iStereoSeparation, 20); + enhancements->Get("StereoConvergence", &iStereoConvergence, 20); + enhancements->Get("StereoSwapEyes", &bStereoSwapEyes, false); IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks"); hacks->Get("EFBAccessEnable", &bEFBAccessEnable, true); @@ -140,9 +146,6 @@ void VideoConfig::GameIniLoad() CHECK_SETTING("Video_Settings", "UseRealXFB", bUseRealXFB); CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples); CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures); - CHECK_SETTING("Video_Settings", "AnaglyphStereo", bAnaglyphStereo); - CHECK_SETTING("Video_Settings", "AnaglyphStereoSeparation", iAnaglyphStereoSeparation); - CHECK_SETTING("Video_Settings", "AnaglyphFocalAngle", iAnaglyphFocalAngle); CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting); CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc); CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode); @@ -179,6 +182,14 @@ void VideoConfig::GameIniLoad() CHECK_SETTING("Video_Enhancements", "ForceFiltering", bForceFiltering); CHECK_SETTING("Video_Enhancements", "MaxAnisotropy", iMaxAnisotropy); // NOTE - this is x in (1 << x) CHECK_SETTING("Video_Enhancements", "PostProcessingShader", sPostProcessingShader); + CHECK_SETTING("Video_Enhancements", "StereoMode", iStereoMode); + CHECK_SETTING("Video_Enhancements", "StereoSeparation", iStereoSeparation); + CHECK_SETTING("Video_Enhancements", "StereoConvergence", iStereoConvergence); + CHECK_SETTING("Video_Enhancements", "StereoSwapEyes", bStereoSwapEyes); + + CHECK_SETTING("Video_Stereoscopy", "StereoMonoEFBDepth", bStereoMonoEFBDepth); + CHECK_SETTING("Video_Stereoscopy", "StereoSeparationPercent", iStereoSeparationPercent); + CHECK_SETTING("Video_Stereoscopy", "StereoConvergencePercent", iStereoConvergencePercent); CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable); CHECK_SETTING("Video_Hacks", "EFBCopyEnable", bEFBCopyEnable); @@ -203,6 +214,7 @@ void VideoConfig::VerifyValidity() // TODO: Check iMaxAnisotropy value if (iAdapter < 0 || iAdapter > ((int)backend_info.Adapters.size() - 1)) iAdapter = 0; if (iMultisampleMode < 0 || iMultisampleMode >= (int)backend_info.AAModes.size()) iMultisampleMode = 0; + if (!backend_info.bSupportsStereoscopy) iStereoMode = 0; } void VideoConfig::Save(const std::string& ini_file) @@ -230,9 +242,6 @@ void VideoConfig::Save(const std::string& ini_file) settings->Set("DumpEFBTarget", bDumpEFBTarget); settings->Set("FreeLook", bFreeLook); settings->Set("UseFFV1", bUseFFV1); - settings->Set("AnaglyphStereo", bAnaglyphStereo); - settings->Set("AnaglyphStereoSeparation", iAnaglyphStereoSeparation); - settings->Set("AnaglyphFocalAngle", iAnaglyphFocalAngle); settings->Set("EnablePixelLighting", bEnablePixelLighting); settings->Set("FastDepthCalc", bFastDepthCalc); settings->Set("ShowEFBCopyRegions", bShowEFBCopyRegions); @@ -250,6 +259,10 @@ void VideoConfig::Save(const std::string& ini_file) enhancements->Set("ForceFiltering", bForceFiltering); enhancements->Set("MaxAnisotropy", iMaxAnisotropy); enhancements->Set("PostProcessingShader", sPostProcessingShader); + enhancements->Set("StereoMode", iStereoMode); + enhancements->Set("StereoSeparation", iStereoSeparation); + enhancements->Set("StereoConvergence", iStereoConvergence); + enhancements->Set("StereoSwapEyes", bStereoSwapEyes); IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks"); hacks->Set("EFBAccessEnable", bEFBAccessEnable); diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 44b0740728..c825f8ec53 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -44,6 +44,14 @@ enum EFBScale SCALE_4X, }; +enum StereoMode +{ + STEREO_OFF = 0, + STEREO_SBS, + STEREO_TAB, + STEREO_ANAGLYPH +}; + // NEVER inherit from this class. struct VideoConfig final { @@ -71,6 +79,10 @@ struct VideoConfig final bool bForceFiltering; int iMaxAnisotropy; std::string sPostProcessingShader; + int iStereoMode; + int iStereoSeparation; + int iStereoConvergence; + bool bStereoSwapEyes; // Information bool bShowFPS; @@ -92,9 +104,6 @@ struct VideoConfig final bool bDumpEFBTarget; bool bUseFFV1; bool bFreeLook; - bool bAnaglyphStereo; - int iAnaglyphStereoSeparation; - int iAnaglyphFocalAngle; bool bBorderlessFullscreen; // Hacks @@ -115,6 +124,11 @@ struct VideoConfig final int iLog; // CONF_ bits int iSaveTargetId; // TODO: Should be dropped + // Stereoscopy + bool bStereoMonoEFBDepth; + int iStereoSeparationPercent; + int iStereoConvergencePercent; + // D3D only config, mostly to be merged into the above int iAdapter; @@ -136,9 +150,11 @@ struct VideoConfig final bool bSupportsDualSourceBlend; bool bSupportsPrimitiveRestart; bool bSupportsOversizedViewports; + bool bSupportsStereoscopy; bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon bool bSupportsBBox; + bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon } backend_info; // Utility |
